summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/AssemblyInfo.cs2
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs1
-rw-r--r--crypto/src/asn1/Asn1Set.cs100
-rw-r--r--crypto/src/asn1/DerOutputStream.cs2
-rw-r--r--crypto/src/asn1/anssi/ANSSINamedCurves.cs123
-rw-r--r--crypto/src/asn1/anssi/ANSSIObjectIdentifiers.cs13
-rw-r--r--crypto/src/asn1/cms/EncryptedData.cs4
-rw-r--r--crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs2
-rw-r--r--crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs27
-rw-r--r--crypto/src/asn1/misc/MiscObjectIdentifiers.cs93
-rw-r--r--crypto/src/asn1/nist/NISTNamedCurves.cs52
-rw-r--r--crypto/src/asn1/nist/NISTObjectIdentifiers.cs6
-rw-r--r--crypto/src/asn1/pkcs/PBKDF2Params.cs196
-rw-r--r--crypto/src/asn1/pkcs/PrivateKeyInfo.cs143
-rw-r--r--crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs145
-rw-r--r--crypto/src/asn1/sec/ECPrivateKeyStructure.cs226
-rw-r--r--crypto/src/asn1/sec/SECNamedCurves.cs169
-rw-r--r--crypto/src/asn1/smime/SMIMECapabilities.cs6
-rw-r--r--crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs70
-rw-r--r--crypto/src/asn1/util/FilterStream.cs2
-rw-r--r--crypto/src/asn1/x509/AlgorithmIdentifier.cs11
-rw-r--r--crypto/src/asn1/x509/ExtendedKeyUsage.cs83
-rw-r--r--crypto/src/asn1/x9/ECNamedCurveTable.cs45
-rw-r--r--crypto/src/asn1/x9/X962NamedCurves.cs58
-rw-r--r--crypto/src/asn1/x9/X9ECParameters.cs98
-rw-r--r--crypto/src/asn1/x9/X9ECParametersHolder.cs17
-rw-r--r--crypto/src/asn1/x9/X9ECPoint.cs54
-rw-r--r--crypto/src/asn1/x9/X9FieldID.cs16
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs18
-rw-r--r--crypto/src/bcpg/DsaPublicBcpgKey.cs102
-rw-r--r--crypto/src/bcpg/ECDHPublicBCPGKey.cs102
-rw-r--r--crypto/src/bcpg/ECDsaPublicBCPGKey.cs34
-rw-r--r--crypto/src/bcpg/ECPublicBCPGKey.cs97
-rw-r--r--crypto/src/bcpg/ECSecretBCPGKey.cs56
-rw-r--r--crypto/src/bcpg/PublicKeyAlgorithmTags.cs30
-rw-r--r--crypto/src/bcpg/PublicKeyEncSessionPacket.cs54
-rw-r--r--crypto/src/bcpg/PublicKeyPacket.cs54
-rw-r--r--crypto/src/bcpg/S2k.cs12
-rw-r--r--crypto/src/bcpg/SignaturePacket.cs5
-rw-r--r--crypto/src/bcpg/SignatureSubpacket.cs50
-rw-r--r--crypto/src/bcpg/SignatureSubpacketsReader.cs84
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacket.cs42
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacketsReader.cs100
-rw-r--r--crypto/src/bcpg/attr/ImageAttrib.cs70
-rw-r--r--crypto/src/bcpg/sig/EmbeddedSignature.cs3
-rw-r--r--crypto/src/bcpg/sig/Exportable.cs9
-rw-r--r--crypto/src/bcpg/sig/Features.cs75
-rw-r--r--crypto/src/bcpg/sig/IssuerKeyId.cs9
-rw-r--r--crypto/src/bcpg/sig/KeyExpirationTime.cs11
-rw-r--r--crypto/src/bcpg/sig/KeyFlags.cs11
-rw-r--r--crypto/src/bcpg/sig/NotationData.cs11
-rw-r--r--crypto/src/bcpg/sig/PreferredAlgorithms.cs21
-rw-r--r--crypto/src/bcpg/sig/PrimaryUserId.cs9
-rw-r--r--crypto/src/bcpg/sig/Revocable.cs13
-rw-r--r--crypto/src/bcpg/sig/RevocationKey.cs9
-rw-r--r--crypto/src/bcpg/sig/RevocationReason.cs14
-rw-r--r--crypto/src/bcpg/sig/SignatureCreationTime.cs16
-rw-r--r--crypto/src/bcpg/sig/SignatureExpirationTime.cs19
-rw-r--r--crypto/src/bcpg/sig/SignerUserId.cs17
-rw-r--r--crypto/src/bcpg/sig/TrustSignature.cs15
-rw-r--r--crypto/src/cms/CMSSignedDataGenerator.cs84
-rw-r--r--crypto/src/cms/CMSSignedDataStreamGenerator.cs12
-rw-r--r--crypto/src/cms/CMSSignedGenerator.cs364
-rw-r--r--crypto/src/cms/CMSSignedHelper.cs115
-rw-r--r--crypto/src/cms/CMSTypedStream.cs1
-rw-r--r--crypto/src/cms/KeyAgreeRecipientInformation.cs410
-rw-r--r--crypto/src/cms/SignerInfoGenerator.cs163
-rw-r--r--crypto/src/cms/SignerInformation.cs7
-rw-r--r--crypto/src/cms/SignerInformationStore.cs53
-rw-r--r--crypto/src/crypto/BufferedAeadBlockCipher.cs2
-rw-r--r--crypto/src/crypto/BufferedBlockCipher.cs24
-rw-r--r--crypto/src/crypto/Check.cs25
-rw-r--r--crypto/src/crypto/IBlockResult.cs24
-rw-r--r--crypto/src/crypto/ISignatureCalculator.cs23
-rw-r--r--crypto/src/crypto/ISignatureVerifier.cs21
-rw-r--r--crypto/src/crypto/ISignatureVerifierProvider.cs18
-rw-r--r--crypto/src/crypto/IStreamCalculator.cs23
-rw-r--r--crypto/src/crypto/IVerifier.cs25
-rw-r--r--crypto/src/crypto/IXof.cs22
-rw-r--r--crypto/src/crypto/OutputLengthException.cs28
-rw-r--r--crypto/src/crypto/agreement/DHStandardGroups.cs206
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Client.cs73
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Server.cs75
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs159
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Utilities.cs70
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs8
-rw-r--r--crypto/src/crypto/digests/KeccakDigest.cs534
-rw-r--r--crypto/src/crypto/digests/SHA3Digest.cs538
-rw-r--r--crypto/src/crypto/digests/ShakeDigest.cs111
-rw-r--r--crypto/src/crypto/ec/CustomNamedCurves.cs640
-rw-r--r--crypto/src/crypto/encodings/Pkcs1Encoding.cs592
-rw-r--r--crypto/src/crypto/engines/AesEngine.cs1030
-rw-r--r--crypto/src/crypto/engines/AesFastEngine.cs1232
-rw-r--r--crypto/src/crypto/engines/AesLightEngine.cs800
-rw-r--r--crypto/src/crypto/engines/BlowfishEngine.cs15
-rw-r--r--crypto/src/crypto/engines/CamelliaEngine.cs21
-rw-r--r--crypto/src/crypto/engines/CamelliaLightEngine.cs21
-rw-r--r--crypto/src/crypto/engines/Cast5Engine.cs11
-rw-r--r--crypto/src/crypto/engines/ChaChaEngine.cs3
-rw-r--r--crypto/src/crypto/engines/DesEdeEngine.cs7
-rw-r--r--crypto/src/crypto/engines/DesEdeWrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/DesEngine.cs11
-rw-r--r--crypto/src/crypto/engines/ElGamalEngine.cs10
-rw-r--r--crypto/src/crypto/engines/GOST28147Engine.cs27
-rw-r--r--crypto/src/crypto/engines/HC128Engine.cs19
-rw-r--r--crypto/src/crypto/engines/HC256Engine.cs19
-rw-r--r--crypto/src/crypto/engines/ISAACEngine.cs17
-rw-r--r--crypto/src/crypto/engines/IdeaEngine.cs26
-rw-r--r--crypto/src/crypto/engines/IesEngine.cs20
-rw-r--r--crypto/src/crypto/engines/NaccacheSternEngine.cs16
-rw-r--r--crypto/src/crypto/engines/NoekeonEngine.cs21
-rw-r--r--crypto/src/crypto/engines/NullEngine.cs21
-rw-r--r--crypto/src/crypto/engines/RC2Engine.cs22
-rw-r--r--crypto/src/crypto/engines/RC2WrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/RC4Engine.cs25
-rw-r--r--crypto/src/crypto/engines/RC532Engine.cs13
-rw-r--r--crypto/src/crypto/engines/RC564Engine.cs13
-rw-r--r--crypto/src/crypto/engines/RC6Engine.cs22
-rw-r--r--crypto/src/crypto/engines/RFC3211WrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/RFC3394WrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/RSABlindedEngine.cs208
-rw-r--r--crypto/src/crypto/engines/RSABlindingEngine.cs10
-rw-r--r--crypto/src/crypto/engines/RSACoreEngine.cs12
-rw-r--r--crypto/src/crypto/engines/RijndaelEngine.cs30
-rw-r--r--crypto/src/crypto/engines/RsaEngine.cs10
-rw-r--r--crypto/src/crypto/engines/SEEDEngine.cs21
-rw-r--r--crypto/src/crypto/engines/Salsa20Engine.cs27
-rw-r--r--crypto/src/crypto/engines/SerpentEngine.cs22
-rw-r--r--crypto/src/crypto/engines/SkipjackEngine.cs26
-rw-r--r--crypto/src/crypto/engines/TEAEngine.cs21
-rw-r--r--crypto/src/crypto/engines/ThreefishEngine.cs12
-rw-r--r--crypto/src/crypto/engines/TwofishEngine.cs9
-rw-r--r--crypto/src/crypto/engines/VMPCEngine.cs11
-rw-r--r--crypto/src/crypto/engines/XSalsa20Engine.cs2
-rw-r--r--crypto/src/crypto/engines/XTEAEngine.cs21
-rw-r--r--crypto/src/crypto/generators/DHKeyGeneratorHelper.cs23
-rw-r--r--crypto/src/crypto/generators/DsaKeyPairGenerator.cs79
-rw-r--r--crypto/src/crypto/generators/DsaParametersGenerator.cs57
-rw-r--r--crypto/src/crypto/generators/ECKeyPairGenerator.cs1
-rw-r--r--crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs123
-rw-r--r--crypto/src/crypto/generators/RsaKeyPairGenerator.cs177
-rw-r--r--crypto/src/crypto/macs/GMac.cs179
-rw-r--r--crypto/src/crypto/macs/Poly1305.cs543
-rw-r--r--crypto/src/crypto/modes/CcmBlockCipher.cs134
-rw-r--r--crypto/src/crypto/modes/CtsBlockCipher.cs2
-rw-r--r--crypto/src/crypto/modes/EAXBlockCipher.cs55
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs378
-rw-r--r--crypto/src/crypto/modes/OCBBlockCipher.cs33
-rw-r--r--crypto/src/crypto/modes/SicBlockCipher.cs176
-rw-r--r--crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs58
-rw-r--r--crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs28
-rw-r--r--crypto/src/crypto/modes/gcm/GcmUtilities.cs344
-rw-r--r--crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs31
-rw-r--r--crypto/src/crypto/operators/Asn1Signature.cs559
-rw-r--r--crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs9
-rw-r--r--crypto/src/crypto/paddings/Pkcs7Padding.cs21
-rw-r--r--crypto/src/crypto/parameters/DHParameters.cs1
-rw-r--r--crypto/src/crypto/parameters/Srp6GroupParameters.cs27
-rw-r--r--crypto/src/crypto/prng/CryptoApiRandomGenerator.cs94
-rw-r--r--crypto/src/crypto/signers/DsaDigestSigner.cs14
-rw-r--r--crypto/src/crypto/signers/DsaSigner.cs45
-rw-r--r--crypto/src/crypto/signers/ECDsaSigner.cs43
-rw-r--r--crypto/src/crypto/signers/ECGOST3410Signer.cs8
-rw-r--r--crypto/src/crypto/signers/ECNRSigner.cs8
-rw-r--r--crypto/src/crypto/signers/GOST3410DigestSigner.cs14
-rw-r--r--crypto/src/crypto/signers/GOST3410Signer.cs8
-rw-r--r--crypto/src/crypto/signers/GenericSigner.cs229
-rw-r--r--crypto/src/crypto/signers/HMacDsaKCalculator.cs150
-rw-r--r--crypto/src/crypto/signers/IDsaKCalculator.cs44
-rw-r--r--crypto/src/crypto/signers/Iso9796d2PssSigner.cs54
-rw-r--r--crypto/src/crypto/signers/Iso9796d2Signer.cs67
-rw-r--r--crypto/src/crypto/signers/IsoTrailers.cs57
-rw-r--r--crypto/src/crypto/signers/PssSigner.cs2
-rw-r--r--crypto/src/crypto/signers/RandomDsaKCalculator.cs44
-rw-r--r--crypto/src/crypto/signers/RsaDigestSigner.cs203
-rw-r--r--crypto/src/crypto/signers/X931Signer.cs225
-rw-r--r--crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCipherFactory.cs15
-rw-r--r--crypto/src/crypto/tls/AbstractTlsClient.cs258
-rw-r--r--crypto/src/crypto/tls/AbstractTlsContext.cs152
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCredentials.cs10
-rw-r--r--crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsKeyExchange.cs166
-rw-r--r--crypto/src/crypto/tls/AbstractTlsPeer.cs48
-rw-r--r--crypto/src/crypto/tls/AbstractTlsServer.cs349
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSigner.cs50
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs20
-rw-r--r--crypto/src/crypto/tls/AlertDescription.cs87
-rw-r--r--crypto/src/crypto/tls/AlertLevel.cs18
-rw-r--r--crypto/src/crypto/tls/AlwaysValidVerifyer.cs24
-rw-r--r--crypto/src/crypto/tls/BasicTlsPskIdentity.cs43
-rw-r--r--crypto/src/crypto/tls/ByteQueueStream.cs114
-rw-r--r--crypto/src/crypto/tls/CertChainType.cs6
-rw-r--r--crypto/src/crypto/tls/Certificate.cs7
-rw-r--r--crypto/src/crypto/tls/CertificateRequest.cs18
-rw-r--r--crypto/src/crypto/tls/CertificateStatus.cs102
-rw-r--r--crypto/src/crypto/tls/CertificateStatusRequest.cs95
-rw-r--r--crypto/src/crypto/tls/CertificateStatusType.cs2
-rw-r--r--crypto/src/crypto/tls/CertificateUrl.cs124
-rw-r--r--crypto/src/crypto/tls/Chacha20Poly1305.cs153
-rw-r--r--crypto/src/crypto/tls/ChangeCipherSpec.cs2
-rw-r--r--crypto/src/crypto/tls/CipherSuite.cs147
-rw-r--r--crypto/src/crypto/tls/ClientAuthenticationType.cs6
-rw-r--r--crypto/src/crypto/tls/CombinedHash.cs203
-rw-r--r--crypto/src/crypto/tls/CompressionMethod.cs2
-rw-r--r--crypto/src/crypto/tls/DatagramTransport.cs23
-rw-r--r--crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs71
-rw-r--r--crypto/src/crypto/tls/DefaultTlsCipherFactory.cs204
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs270
-rw-r--r--crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs51
-rw-r--r--crypto/src/crypto/tls/DefaultTlsServer.cs162
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs94
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs70
-rw-r--r--crypto/src/crypto/tls/DeferredHash.cs201
-rw-r--r--crypto/src/crypto/tls/DigestAlgorithm.cs24
-rw-r--r--crypto/src/crypto/tls/DigitallySigned.cs70
-rw-r--r--crypto/src/crypto/tls/DtlsClientProtocol.cs839
-rw-r--r--crypto/src/crypto/tls/DtlsEpoch.cs51
-rw-r--r--crypto/src/crypto/tls/DtlsHandshakeRetransmit.cs11
-rw-r--r--crypto/src/crypto/tls/DtlsProtocol.cs92
-rw-r--r--crypto/src/crypto/tls/DtlsReassembler.cs125
-rw-r--r--crypto/src/crypto/tls/DtlsRecordLayer.cs507
-rw-r--r--crypto/src/crypto/tls/DtlsReliableHandshake.cs443
-rw-r--r--crypto/src/crypto/tls/DtlsReplayWindow.cs85
-rw-r--r--crypto/src/crypto/tls/DtlsServerProtocol.cs655
-rw-r--r--crypto/src/crypto/tls/DtlsTransport.cs77
-rw-r--r--crypto/src/crypto/tls/ECBasisType.cs6
-rw-r--r--crypto/src/crypto/tls/EncryptionAlgorithm.cs17
-rw-r--r--crypto/src/crypto/tls/ExporterLabel.cs11
-rw-r--r--crypto/src/crypto/tls/ExtensionType.cs18
-rw-r--r--crypto/src/crypto/tls/FiniteFieldDheGroup.cs21
-rw-r--r--crypto/src/crypto/tls/HeartbeatExtension.cs52
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessage.cs102
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessageType.cs6
-rw-r--r--crypto/src/crypto/tls/HeartbeatMode.cs6
-rw-r--r--crypto/src/crypto/tls/ICertificateVerifyer.cs18
-rw-r--r--crypto/src/crypto/tls/LegacyTlsAuthentication.cs30
-rw-r--r--crypto/src/crypto/tls/LegacyTlsClient.cs26
-rw-r--r--crypto/src/crypto/tls/MaxFragmentLength.cs10
-rw-r--r--crypto/src/crypto/tls/NameType.cs2
-rw-r--r--crypto/src/crypto/tls/NamedCurve.cs23
-rw-r--r--crypto/src/crypto/tls/NewSessionTicket.cs53
-rw-r--r--crypto/src/crypto/tls/OcspStatusRequest.cs130
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs204
-rw-r--r--crypto/src/crypto/tls/PskTlsServer.cs93
-rw-r--r--crypto/src/crypto/tls/RecordStream.cs360
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs119
-rw-r--r--crypto/src/crypto/tls/ServerDHParams.cs61
-rw-r--r--crypto/src/crypto/tls/ServerName.cs105
-rw-r--r--crypto/src/crypto/tls/ServerNameList.cs81
-rw-r--r--crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs15
-rw-r--r--crypto/src/crypto/tls/ServerSrpParams.cs75
-rw-r--r--crypto/src/crypto/tls/SessionParameters.cs165
-rw-r--r--crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs69
-rw-r--r--crypto/src/crypto/tls/SrpTlsClient.cs197
-rw-r--r--crypto/src/crypto/tls/SrpTlsServer.cs121
-rw-r--r--crypto/src/crypto/tls/Ssl3Mac.cs206
-rw-r--r--crypto/src/crypto/tls/SupplementalDataEntry.cs26
-rw-r--r--crypto/src/crypto/tls/TlsAeadCipher.cs189
-rw-r--r--crypto/src/crypto/tls/TlsAgreementCredentials.cs11
-rw-r--r--crypto/src/crypto/tls/TlsBlockCipher.cs345
-rw-r--r--crypto/src/crypto/tls/TlsCipher.cs6
-rw-r--r--crypto/src/crypto/tls/TlsCipherFactory.cs3
-rw-r--r--crypto/src/crypto/tls/TlsClient.cs60
-rw-r--r--crypto/src/crypto/tls/TlsClientContext.cs12
-rw-r--r--crypto/src/crypto/tls/TlsClientContextImpl.cs41
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs906
-rw-r--r--crypto/src/crypto/tls/TlsContext.cs45
-rw-r--r--crypto/src/crypto/tls/TlsDHKeyExchange.cs219
-rw-r--r--crypto/src/crypto/tls/TlsDHUtilities.cs441
-rw-r--r--crypto/src/crypto/tls/TlsDheKeyExchange.cs88
-rw-r--r--crypto/src/crypto/tls/TlsDsaSigner.cs75
-rw-r--r--crypto/src/crypto/tls/TlsDssSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsECDHKeyExchange.cs251
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs125
-rw-r--r--crypto/src/crypto/tls/TlsECDsaSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsEccUtilities.cs718
-rw-r--r--crypto/src/crypto/tls/TlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/TlsExtensionsUtilities.cs258
-rw-r--r--crypto/src/crypto/tls/TlsFatalAlert.cs6
-rw-r--r--crypto/src/crypto/tls/TlsHandshakeHash.cs22
-rw-r--r--crypto/src/crypto/tls/TlsKeyExchange.cs80
-rw-r--r--crypto/src/crypto/tls/TlsMac.cs201
-rw-r--r--crypto/src/crypto/tls/TlsNullCipher.cs108
-rw-r--r--crypto/src/crypto/tls/TlsPeer.cs43
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs1318
-rw-r--r--crypto/src/crypto/tls/TlsProtocolHandler.cs1217
-rw-r--r--crypto/src/crypto/tls/TlsPskIdentityManager.cs11
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs310
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs161
-rw-r--r--crypto/src/crypto/tls/TlsRsaSigner.cs82
-rw-r--r--crypto/src/crypto/tls/TlsRsaUtilities.cs152
-rw-r--r--crypto/src/crypto/tls/TlsServer.cs93
-rw-r--r--crypto/src/crypto/tls/TlsServerContext.cs11
-rw-r--r--crypto/src/crypto/tls/TlsServerContextImpl.cs20
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs803
-rw-r--r--crypto/src/crypto/tls/TlsSession.cs15
-rw-r--r--crypto/src/crypto/tls/TlsSessionImpl.cs54
-rw-r--r--crypto/src/crypto/tls/TlsSigner.cs23
-rw-r--r--crypto/src/crypto/tls/TlsSignerCredentials.cs13
-rw-r--r--crypto/src/crypto/tls/TlsSrpGroupVerifier.cs17
-rw-r--r--crypto/src/crypto/tls/TlsSrpIdentityManager.cs21
-rw-r--r--crypto/src/crypto/tls/TlsSrpKeyExchange.cs286
-rw-r--r--crypto/src/crypto/tls/TlsSrpLoginParameters.cs36
-rw-r--r--crypto/src/crypto/tls/TlsSrpUtilities.cs74
-rw-r--r--crypto/src/crypto/tls/TlsSrtpUtilities.cs62
-rw-r--r--crypto/src/crypto/tls/TlsStream.cs119
-rw-r--r--crypto/src/crypto/tls/TlsStreamCipher.cs176
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs2023
-rw-r--r--crypto/src/crypto/tls/UrlAndHash.cs94
-rw-r--r--crypto/src/crypto/tls/UseSrtpData.cs56
-rw-r--r--crypto/src/crypto/tls/UserMappingType.cs2
-rw-r--r--crypto/src/crypto/util/Pack.cs25
-rw-r--r--crypto/src/math/Primes.cs584
-rw-r--r--crypto/src/math/ec/ECAlgorithms.cs88
-rw-r--r--crypto/src/math/ec/ECCurve.cs523
-rw-r--r--crypto/src/math/ec/ECFieldElement.cs28
-rw-r--r--crypto/src/math/ec/ECPoint.cs513
-rw-r--r--crypto/src/math/ec/LongArray.cs2
-rw-r--r--crypto/src/math/ec/abc/Tnaf.cs171
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519.cs29
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Field.cs2
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP128R1Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP128R1Field.cs218
-rw-r--r--crypto/src/math/ec/custom/sec/SecP128R1FieldElement.cs198
-rw-r--r--crypto/src/math/ec/custom/sec/SecP128R1Point.cs279
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160K1Curve.cs74
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160K1Point.cs269
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R1Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R1Field.cs186
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R1FieldElement.cs203
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R1Point.cs279
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R2Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R2Field.cs178
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R2FieldElement.cs218
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R2Point.cs279
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Field.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs1
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Point.cs17
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113Field.cs209
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113R1Point.cs281
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113R2Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113R2Point.cs291
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131Field.cs303
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131R1Point.cs287
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131R2Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131R2Point.cs283
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163Field.cs313
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163K1Curve.cs104
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163K1Point.cs289
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163R1Point.cs283
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163R2Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163R2Point.cs290
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193Field.cs282
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193R1Point.cs283
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193R2Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193R2Point.cs283
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233Field.cs276
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233K1Curve.cs104
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233K1Point.cs302
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233R1Point.cs282
-rw-r--r--crypto/src/math/ec/custom/sec/SecT239Field.cs286
-rw-r--r--crypto/src/math/ec/custom/sec/SecT239FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT239K1Curve.cs104
-rw-r--r--crypto/src/math/ec/custom/sec/SecT239K1Point.cs297
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283Field.cs370
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283K1Curve.cs104
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283K1Point.cs296
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283R1Point.cs282
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409Field.cs295
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409K1Curve.cs104
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409K1Point.cs296
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409R1Curve.cs98
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409R1Point.cs282
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571Field.cs302
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571FieldElement.cs214
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571K1Curve.cs104
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571K1Point.cs296
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571R1Curve.cs102
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571R1Point.cs286
-rw-r--r--crypto/src/math/ec/multiplier/AbstractECMultiplier.cs8
-rw-r--r--crypto/src/math/ec/multiplier/ReferenceMultiplier.cs28
-rw-r--r--crypto/src/math/ec/multiplier/WNafUtilities.cs101
-rw-r--r--crypto/src/math/ec/multiplier/WTauNafMultiplier.cs71
-rw-r--r--crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs6
-rw-r--r--crypto/src/math/raw/Interleave.cs95
-rw-r--r--crypto/src/math/raw/Mod.cs (renamed from crypto/src/math/ec/Mod.cs)26
-rw-r--r--crypto/src/math/raw/Nat.cs (renamed from crypto/src/math/ec/Nat.cs)42
-rw-r--r--crypto/src/math/raw/Nat128.cs856
-rw-r--r--crypto/src/math/raw/Nat160.cs874
-rw-r--r--crypto/src/math/raw/Nat192.cs (renamed from crypto/src/math/ec/custom/sec/Nat192.cs)88
-rw-r--r--crypto/src/math/raw/Nat224.cs (renamed from crypto/src/math/ec/custom/sec/Nat224.cs)2
-rw-r--r--crypto/src/math/raw/Nat256.cs (renamed from crypto/src/math/ec/custom/sec/Nat256.cs)89
-rw-r--r--crypto/src/math/raw/Nat320.cs98
-rw-r--r--crypto/src/math/raw/Nat384.cs (renamed from crypto/src/math/ec/custom/sec/Nat384.cs)2
-rw-r--r--crypto/src/math/raw/Nat448.cs100
-rw-r--r--crypto/src/math/raw/Nat512.cs (renamed from crypto/src/math/ec/custom/sec/Nat512.cs)2
-rw-r--r--crypto/src/math/raw/Nat576.cs102
-rw-r--r--crypto/src/openpgp/PgpEncryptedDataGenerator.cs154
-rw-r--r--crypto/src/openpgp/PgpKeyPair.cs2
-rw-r--r--crypto/src/openpgp/PgpPad.cs45
-rw-r--r--crypto/src/openpgp/PgpPrivateKey.cs37
-rw-r--r--crypto/src/openpgp/PgpPublicKey.cs967
-rw-r--r--crypto/src/openpgp/PgpPublicKeyEncryptedData.cs140
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRing.cs176
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs235
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs23
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketVector.cs12
-rw-r--r--crypto/src/openpgp/PgpUtilities.cs29
-rw-r--r--crypto/src/openpgp/Rfc6637Utilities.cs138
-rw-r--r--crypto/src/openpgp/SXprUtilities.cs102
-rw-r--r--crypto/src/openpgp/WrappedGeneratorStream.cs2
-rw-r--r--crypto/src/openssl/MiscPemGenerator.cs470
-rw-r--r--crypto/src/openssl/PEMReader.cs3
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequest.cs257
-rw-r--r--crypto/src/pkcs/Pkcs12Store.cs465
-rw-r--r--crypto/src/pkix/PkixCertPathChecker.cs4
-rw-r--r--crypto/src/pkix/PkixParameters.cs4
-rw-r--r--crypto/src/security/AgreementUtilities.cs10
-rw-r--r--crypto/src/security/DigestUtilities.cs24
-rw-r--r--crypto/src/security/DotNetUtilities.cs4
-rw-r--r--crypto/src/security/PrivateKeyFactory.cs42
-rw-r--r--crypto/src/security/SecureRandom.cs448
-rw-r--r--crypto/src/security/SignerUtilities.cs749
-rw-r--r--crypto/src/util/Arrays.cs281
-rw-r--r--crypto/src/util/Times.cs14
-rw-r--r--crypto/src/util/io/FilterStream.cs66
-rw-r--r--crypto/src/util/io/Streams.cs6
-rw-r--r--crypto/src/x509/X509Certificate.cs35
-rw-r--r--crypto/src/x509/X509Crl.cs51
-rw-r--r--crypto/src/x509/X509V1CertificateGenerator.cs50
-rw-r--r--crypto/src/x509/X509V2AttributeCertificate.cs42
-rw-r--r--crypto/src/x509/X509V2AttributeCertificateGenerator.cs69
-rw-r--r--crypto/src/x509/X509V2CRLGenerator.cs92
-rw-r--r--crypto/src/x509/X509V3CertificateGenerator.cs65
479 files changed, 47863 insertions, 12589 deletions
diff --git a/crypto/src/AssemblyInfo.cs b/crypto/src/AssemblyInfo.cs

index 7dd625878..4a813bc5a 100644 --- a/crypto/src/AssemblyInfo.cs +++ b/crypto/src/AssemblyInfo.cs
@@ -14,7 +14,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("The Legion of the Bouncy Castle Inc.")] [assembly: AssemblyProduct("Bouncy Castle for .NET")] -[assembly: AssemblyCopyright("Copyright (C) 2000-2014")] +[assembly: AssemblyCopyright("Copyright (C) 2000-2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 18d13c32d..501e788a0 100644 --- a/crypto/src/asn1/Asn1InputStream.cs +++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -2,7 +2,6 @@ using System; using System.Diagnostics; using System.IO; -using Org.BouncyCastle.Asn1.Utilities; using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Asn1 diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index 2e77ca2a9..cf039d7fe 100644 --- a/crypto/src/asn1/Asn1Set.cs +++ b/crypto/src/asn1/Asn1Set.cs
@@ -278,67 +278,30 @@ namespace Org.BouncyCastle.Asn1 return encObj; } - /** - * return true if a &lt;= b (arrays are assumed padded with zeros). - */ - private bool LessThanOrEqual( - byte[] a, - byte[] b) + protected internal void Sort() { - int len = System.Math.Min(a.Length, b.Length); - for (int i = 0; i != len; ++i) + if (_set.Count < 2) + return; + + Asn1Encodable[] items = new Asn1Encodable[_set.Count]; + byte[][] keys = new byte[_set.Count][]; + + for (int i = 0; i < _set.Count; ++i) { - if (a[i] != b[i]) - { - return a[i] < b[i]; - } + Asn1Encodable item = (Asn1Encodable)_set[i]; + items[i] = item; + keys[i] = item.GetEncoded(Asn1Encodable.Der); } - return len == a.Length; - } - protected internal void Sort() - { - if (_set.Count > 1) - { - bool swapped = true; - int lastSwap = _set.Count - 1; + Array.Sort(keys, items, new DerComparer()); - while (swapped) - { - int index = 0; - int swapIndex = 0; - byte[] a = ((Asn1Encodable) _set[0]).GetEncoded(); - - swapped = false; - - while (index != lastSwap) - { - byte[] b = ((Asn1Encodable) _set[index + 1]).GetEncoded(); - - if (LessThanOrEqual(a, b)) - { - a = b; - } - else - { - object o = _set[index]; - _set[index] = _set[index + 1]; - _set[index + 1] = o; - - swapped = true; - swapIndex = index; - } - - index++; - } - - lastSwap = swapIndex; - } + for (int i = 0; i < _set.Count; ++i) + { + _set[i] = items[i]; } } - protected internal void AddObject( - Asn1Encodable obj) + protected internal void AddObject(Asn1Encodable obj) { _set.Add(obj); } @@ -347,5 +310,36 @@ namespace Org.BouncyCastle.Asn1 { return CollectionUtilities.ToString(_set); } + + private class DerComparer + : IComparer + { + public int Compare(object x, object y) + { + byte[] a = (byte[])x, b = (byte[])y; + int len = System.Math.Min(a.Length, b.Length); + for (int i = 0; i != len; ++i) + { + byte ai = a[i], bi = b[i]; + if (ai != bi) + return ai < bi ? -1 : 1; + } + if (a.Length > b.Length) + return AllZeroesFrom(a, len) ? 0 : 1; + if (a.Length < b.Length) + return AllZeroesFrom(b, len) ? 0 : -1; + return 0; + } + + private bool AllZeroesFrom(byte[] bs, int pos) + { + while (pos < bs.Length) + { + if (bs[pos++] != 0) + return false; + } + return true; + } + } } } diff --git a/crypto/src/asn1/DerOutputStream.cs b/crypto/src/asn1/DerOutputStream.cs
index f95d123f9..c03d9dc11 100644 --- a/crypto/src/asn1/DerOutputStream.cs +++ b/crypto/src/asn1/DerOutputStream.cs
@@ -1,7 +1,7 @@ using System; using System.IO; -using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Asn1 { diff --git a/crypto/src/asn1/anssi/ANSSINamedCurves.cs b/crypto/src/asn1/anssi/ANSSINamedCurves.cs new file mode 100644
index 000000000..c7f9545f2 --- /dev/null +++ b/crypto/src/asn1/anssi/ANSSINamedCurves.cs
@@ -0,0 +1,123 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.Anssi +{ + public class AnssiNamedCurves + { + private static ECCurve ConfigureCurve(ECCurve curve) + { + return curve; + } + + private static BigInteger FromHex(string hex) + { + return new BigInteger(1, Hex.Decode(hex)); + } + + /* + * FRP256v1 + */ + internal class Frp256v1Holder + : X9ECParametersHolder + { + private Frp256v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Frp256v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger p = FromHex("F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03"); + BigInteger a = FromHex("F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C00"); + BigInteger b = FromHex("EE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F"); + byte[] S = null; + BigInteger n = FromHex("F1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1"); + BigInteger h = BigInteger.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "B6B3D4C356C139EB31183D4749D423958C27D2DCAF98B70164C97A2DD98F5CFF" + + "6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + + private static readonly IDictionary objIds = Platform.CreateHashtable(); + private static readonly IDictionary curves = Platform.CreateHashtable(); + private static readonly IDictionary names = Platform.CreateHashtable(); + + private static void DefineCurve( + string name, + DerObjectIdentifier oid, + X9ECParametersHolder holder) + { + objIds.Add(Platform.ToLowerInvariant(name), oid); + names.Add(oid, name); + curves.Add(oid, holder); + } + + static AnssiNamedCurves() + { + DefineCurve("FRP256v1", AnssiObjectIdentifiers.FRP256v1, Frp256v1Holder.Instance); + } + + public static X9ECParameters GetByName( + string name) + { + DerObjectIdentifier oid = GetOid(name); + return oid == null ? null : GetByOid(oid); + } + + /** + * return the X9ECParameters object for the named curve represented by + * the passed in object identifier. Null if the curve isn't present. + * + * @param oid an object identifier representing a named curve, if present. + */ + public static X9ECParameters GetByOid( + DerObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder)curves[oid]; + return holder == null ? null : holder.Parameters; + } + + /** + * return the object identifier signified by the passed in name. Null + * if there is no object identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier)objIds[Platform.ToLowerInvariant(name)]; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName( + DerObjectIdentifier oid) + { + return (string)names[oid]; + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(names.Values); } + } + } +} diff --git a/crypto/src/asn1/anssi/ANSSIObjectIdentifiers.cs b/crypto/src/asn1/anssi/ANSSIObjectIdentifiers.cs new file mode 100644
index 000000000..d230832b5 --- /dev/null +++ b/crypto/src/asn1/anssi/ANSSIObjectIdentifiers.cs
@@ -0,0 +1,13 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Anssi +{ + public sealed class AnssiObjectIdentifiers + { + private AnssiObjectIdentifiers() + { + } + + public static readonly DerObjectIdentifier FRP256v1 = new DerObjectIdentifier("1.2.250.1.223.101.256.1"); + } +} diff --git a/crypto/src/asn1/cms/EncryptedData.cs b/crypto/src/asn1/cms/EncryptedData.cs
index 5b8378282..cb109a640 100644 --- a/crypto/src/asn1/cms/EncryptedData.cs +++ b/crypto/src/asn1/cms/EncryptedData.cs
@@ -52,8 +52,8 @@ namespace Org.BouncyCastle.Asn1.Cms if (seq.Count > 2) { - this.unprotectedAttrs = Asn1Set.GetInstance(seq[2]); - } + this.unprotectedAttrs = Asn1Set.GetInstance((Asn1TaggedObject)seq[2], false); + } } public virtual DerInteger Version diff --git a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
index ca57c283d..32d3103af 100644 --- a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs +++ b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
@@ -150,7 +150,7 @@ namespace Org.BouncyCastle.Asn1.CryptoPro */ public static IEnumerable Names { - get { return new EnumerableProxy(objIds.Keys); } + get { return new EnumerableProxy(names.Values); } } public static ECDomainParameters GetByName( diff --git a/crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs b/crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs
index b8aba7ee9..bc48c3fa2 100644 --- a/crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs +++ b/crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs
@@ -2,17 +2,18 @@ using System; namespace Org.BouncyCastle.Asn1.Microsoft { - public abstract class MicrosoftObjectIdentifiers - { - // - // Microsoft - // iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) Microsoft(311) - // - public static readonly DerObjectIdentifier Microsoft = new DerObjectIdentifier("1.3.6.1.4.1.311"); - public static readonly DerObjectIdentifier MicrosoftCertTemplateV1 = new DerObjectIdentifier(Microsoft + ".20.2"); - public static readonly DerObjectIdentifier MicrosoftCAVersion = new DerObjectIdentifier(Microsoft + ".21.1"); - public static readonly DerObjectIdentifier MicrosoftPrevCACertHash = new DerObjectIdentifier(Microsoft + ".21.2"); - public static readonly DerObjectIdentifier MicrosoftCertTemplateV2 = new DerObjectIdentifier(Microsoft + ".21.7"); - public static readonly DerObjectIdentifier MicrosoftAppPolicies = new DerObjectIdentifier(Microsoft + ".21.10"); - } + public abstract class MicrosoftObjectIdentifiers + { + // + // Microsoft + // iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) Microsoft(311) + // + public static readonly DerObjectIdentifier Microsoft = new DerObjectIdentifier("1.3.6.1.4.1.311"); + public static readonly DerObjectIdentifier MicrosoftCertTemplateV1 = Microsoft.Branch("20.2"); + public static readonly DerObjectIdentifier MicrosoftCAVersion = Microsoft.Branch("21.1"); + public static readonly DerObjectIdentifier MicrosoftPrevCACertHash = Microsoft.Branch("21.2"); + public static readonly DerObjectIdentifier MicrosoftCrlNextPublish = Microsoft.Branch("21.4"); + public static readonly DerObjectIdentifier MicrosoftCertTemplateV2 = Microsoft.Branch("21.7"); + public static readonly DerObjectIdentifier MicrosoftAppPolicies = Microsoft.Branch("21.10"); + } } diff --git a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
index 01004d889..8128b6952 100644 --- a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs +++ b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
@@ -1,5 +1,3 @@ -using Org.BouncyCastle.Asn1; - namespace Org.BouncyCastle.Asn1.Misc { public abstract class MiscObjectIdentifiers @@ -9,40 +7,73 @@ namespace Org.BouncyCastle.Asn1.Misc // iso/itu(2) joint-assign(16) us(840) uscompany(1) Netscape(113730) cert-extensions(1) } // public static readonly DerObjectIdentifier Netscape = new DerObjectIdentifier("2.16.840.1.113730.1"); - public static readonly DerObjectIdentifier NetscapeCertType = new DerObjectIdentifier(Netscape + ".1"); - public static readonly DerObjectIdentifier NetscapeBaseUrl = new DerObjectIdentifier(Netscape + ".2"); - public static readonly DerObjectIdentifier NetscapeRevocationUrl = new DerObjectIdentifier(Netscape + ".3"); - public static readonly DerObjectIdentifier NetscapeCARevocationUrl = new DerObjectIdentifier(Netscape + ".4"); - public static readonly DerObjectIdentifier NetscapeRenewalUrl = new DerObjectIdentifier(Netscape + ".7"); - public static readonly DerObjectIdentifier NetscapeCAPolicyUrl = new DerObjectIdentifier(Netscape + ".8"); - public static readonly DerObjectIdentifier NetscapeSslServerName = new DerObjectIdentifier(Netscape + ".12"); - public static readonly DerObjectIdentifier NetscapeCertComment = new DerObjectIdentifier(Netscape + ".13"); + public static readonly DerObjectIdentifier NetscapeCertType = Netscape.Branch("1"); + public static readonly DerObjectIdentifier NetscapeBaseUrl = Netscape.Branch("2"); + public static readonly DerObjectIdentifier NetscapeRevocationUrl = Netscape.Branch("3"); + public static readonly DerObjectIdentifier NetscapeCARevocationUrl = Netscape.Branch("4"); + public static readonly DerObjectIdentifier NetscapeRenewalUrl = Netscape.Branch("7"); + public static readonly DerObjectIdentifier NetscapeCAPolicyUrl = Netscape.Branch("8"); + public static readonly DerObjectIdentifier NetscapeSslServerName = Netscape.Branch("12"); + public static readonly DerObjectIdentifier NetscapeCertComment = Netscape.Branch("13"); + // // Verisign // iso/itu(2) joint-assign(16) us(840) uscompany(1) verisign(113733) cert-extensions(1) } // - internal const string Verisign = "2.16.840.1.113733.1"; + public static readonly DerObjectIdentifier Verisign = new DerObjectIdentifier("2.16.840.1.113733.1"); - // + // // CZAG - country, zip, age, and gender // - public static readonly DerObjectIdentifier VerisignCzagExtension = new DerObjectIdentifier(Verisign + ".6.3"); - - // D&B D-U-N-S number - public static readonly DerObjectIdentifier VerisignDnbDunsNumber = new DerObjectIdentifier(Verisign + ".6.15"); - - // - // Novell - // iso/itu(2) country(16) us(840) organization(1) novell(113719) - // - public static readonly string Novell = "2.16.840.1.113719"; - public static readonly DerObjectIdentifier NovellSecurityAttribs = new DerObjectIdentifier(Novell + ".1.9.4.1"); - - // - // Entrust - // iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7) - // - public static readonly string Entrust = "1.2.840.113533.7"; - public static readonly DerObjectIdentifier EntrustVersionExtension = new DerObjectIdentifier(Entrust + ".65.0"); - } + public static readonly DerObjectIdentifier VerisignCzagExtension = Verisign.Branch("6.3"); + + public static readonly DerObjectIdentifier VerisignPrivate_6_9 = Verisign.Branch("6.9"); + public static readonly DerObjectIdentifier VerisignOnSiteJurisdictionHash = Verisign.Branch("6.11"); + public static readonly DerObjectIdentifier VerisignBitString_6_13 = Verisign.Branch("6.13"); + + // D&B D-U-N-S number + public static readonly DerObjectIdentifier VerisignDnbDunsNumber = Verisign.Branch("6.15"); + + public static readonly DerObjectIdentifier VerisignIssStrongCrypto = Verisign.Branch("8.1"); + + // + // Novell + // iso/itu(2) country(16) us(840) organization(1) novell(113719) + // + public static readonly string Novell = "2.16.840.1.113719"; + public static readonly DerObjectIdentifier NovellSecurityAttribs = new DerObjectIdentifier(Novell + ".1.9.4.1"); + + // + // Entrust + // iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7) + // + public static readonly string Entrust = "1.2.840.113533.7"; + public static readonly DerObjectIdentifier EntrustVersionExtension = new DerObjectIdentifier(Entrust + ".65.0"); + + // + // Ascom + // + public static readonly DerObjectIdentifier as_sys_sec_alg_ideaCBC = new DerObjectIdentifier("1.3.6.1.4.1.188.7.1.1.2"); + + // + // Peter Gutmann's Cryptlib + // + public static readonly DerObjectIdentifier cryptlib = new DerObjectIdentifier("1.3.6.1.4.1.3029"); + + public static readonly DerObjectIdentifier cryptlib_algorithm = cryptlib.Branch("1"); + public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_ECB = cryptlib_algorithm.Branch("1.1"); + public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_CBC = cryptlib_algorithm.Branch("1.2"); + public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_CFB = cryptlib_algorithm.Branch("1.3"); + public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_OFB = cryptlib_algorithm.Branch("1.4"); + + // + // Blake2b + // + public static readonly DerObjectIdentifier blake2 = new DerObjectIdentifier("1.3.6.1.4.1.1722.12.2"); + + public static readonly DerObjectIdentifier id_blake2b160 = blake2.Branch("1.5"); + public static readonly DerObjectIdentifier id_blake2b256 = blake2.Branch("1.8"); + public static readonly DerObjectIdentifier id_blake2b384 = blake2.Branch("1.12"); + public static readonly DerObjectIdentifier id_blake2b512 = blake2.Branch("1.16"); + } } diff --git a/crypto/src/asn1/nist/NISTNamedCurves.cs b/crypto/src/asn1/nist/NISTNamedCurves.cs
index 0e82dda7a..f6c1598c6 100644 --- a/crypto/src/asn1/nist/NISTNamedCurves.cs +++ b/crypto/src/asn1/nist/NISTNamedCurves.cs
@@ -21,45 +21,40 @@ namespace Org.BouncyCastle.Asn1.Nist private static readonly IDictionary objIds = Platform.CreateHashtable(); private static readonly IDictionary names = Platform.CreateHashtable(); - private static void DefineCurve( + private static void DefineCurveAlias( string name, DerObjectIdentifier oid) { - objIds.Add(name, oid); + objIds.Add(Platform.ToUpperInvariant(name), oid); names.Add(oid, name); } static NistNamedCurves() { - DefineCurve("B-571", SecObjectIdentifiers.SecT571r1); - DefineCurve("B-409", SecObjectIdentifiers.SecT409r1); - DefineCurve("B-283", SecObjectIdentifiers.SecT283r1); - DefineCurve("B-233", SecObjectIdentifiers.SecT233r1); - DefineCurve("B-163", SecObjectIdentifiers.SecT163r2); - DefineCurve("K-571", SecObjectIdentifiers.SecT571k1); - DefineCurve("K-409", SecObjectIdentifiers.SecT409k1); - DefineCurve("K-283", SecObjectIdentifiers.SecT283k1); - DefineCurve("K-233", SecObjectIdentifiers.SecT233k1); - DefineCurve("K-163", SecObjectIdentifiers.SecT163k1); - DefineCurve("P-521", SecObjectIdentifiers.SecP521r1); - DefineCurve("P-384", SecObjectIdentifiers.SecP384r1); - DefineCurve("P-256", SecObjectIdentifiers.SecP256r1); - DefineCurve("P-224", SecObjectIdentifiers.SecP224r1); - DefineCurve("P-192", SecObjectIdentifiers.SecP192r1); + DefineCurveAlias("B-163", SecObjectIdentifiers.SecT163r2); + DefineCurveAlias("B-233", SecObjectIdentifiers.SecT233r1); + DefineCurveAlias("B-283", SecObjectIdentifiers.SecT283r1); + DefineCurveAlias("B-409", SecObjectIdentifiers.SecT409r1); + DefineCurveAlias("B-571", SecObjectIdentifiers.SecT571r1); + + DefineCurveAlias("K-163", SecObjectIdentifiers.SecT163k1); + DefineCurveAlias("K-233", SecObjectIdentifiers.SecT233k1); + DefineCurveAlias("K-283", SecObjectIdentifiers.SecT283k1); + DefineCurveAlias("K-409", SecObjectIdentifiers.SecT409k1); + DefineCurveAlias("K-571", SecObjectIdentifiers.SecT571k1); + + DefineCurveAlias("P-192", SecObjectIdentifiers.SecP192r1); + DefineCurveAlias("P-224", SecObjectIdentifiers.SecP224r1); + DefineCurveAlias("P-256", SecObjectIdentifiers.SecP256r1); + DefineCurveAlias("P-384", SecObjectIdentifiers.SecP384r1); + DefineCurveAlias("P-521", SecObjectIdentifiers.SecP521r1); } public static X9ECParameters GetByName( string name) { - DerObjectIdentifier oid = (DerObjectIdentifier) objIds[ - Platform.ToUpperInvariant(name)]; - - if (oid != null) - { - return GetByOid(oid); - } - - return null; + DerObjectIdentifier oid = GetOid(name); + return oid == null ? null : GetByOid(oid); } /** @@ -83,8 +78,7 @@ namespace Org.BouncyCastle.Asn1.Nist public static DerObjectIdentifier GetOid( string name) { - return (DerObjectIdentifier) objIds[ - Platform.ToUpperInvariant(name)]; + return (DerObjectIdentifier) objIds[Platform.ToUpperInvariant(name)]; } /** @@ -102,7 +96,7 @@ namespace Org.BouncyCastle.Asn1.Nist */ public static IEnumerable Names { - get { return new EnumerableProxy(objIds.Keys); } + get { return new EnumerableProxy(names.Values); } } } } diff --git a/crypto/src/asn1/nist/NISTObjectIdentifiers.cs b/crypto/src/asn1/nist/NISTObjectIdentifiers.cs
index 8eb5ed437..55b9d8e68 100644 --- a/crypto/src/asn1/nist/NISTObjectIdentifiers.cs +++ b/crypto/src/asn1/nist/NISTObjectIdentifiers.cs
@@ -25,6 +25,12 @@ namespace Org.BouncyCastle.Asn1.Nist public static readonly DerObjectIdentifier IdSha224 = HashAlgs.Branch("4"); public static readonly DerObjectIdentifier IdSha512_224 = HashAlgs.Branch("5"); public static readonly DerObjectIdentifier IdSha512_256 = HashAlgs.Branch("6"); + public static readonly DerObjectIdentifier IdSha3_224 = HashAlgs.Branch("7"); + public static readonly DerObjectIdentifier IdSha3_256 = HashAlgs.Branch("8"); + public static readonly DerObjectIdentifier IdSha3_384 = HashAlgs.Branch("9"); + public static readonly DerObjectIdentifier IdSha3_512 = HashAlgs.Branch("10"); + public static readonly DerObjectIdentifier IdShake128 = HashAlgs.Branch("11"); + public static readonly DerObjectIdentifier IdShake256 = HashAlgs.Branch("12"); public static readonly DerObjectIdentifier Aes = new DerObjectIdentifier(NistAlgorithm + ".1"); diff --git a/crypto/src/asn1/pkcs/PBKDF2Params.cs b/crypto/src/asn1/pkcs/PBKDF2Params.cs
index 1351b94cf..5d1e9854f 100644 --- a/crypto/src/asn1/pkcs/PBKDF2Params.cs +++ b/crypto/src/asn1/pkcs/PBKDF2Params.cs
@@ -1,50 +1,73 @@ using System; - +using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Asn1.Pkcs { - public class Pbkdf2Params - : Asn1Encodable - { - private readonly Asn1OctetString octStr; - private readonly DerInteger iterationCount; - private readonly DerInteger keyLength; - - public static Pbkdf2Params GetInstance( - object obj) - { - if (obj == null || obj is Pbkdf2Params) - return (Pbkdf2Params)obj; - - if (obj is Asn1Sequence) - return new Pbkdf2Params((Asn1Sequence)obj); - - throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); - } - - public Pbkdf2Params( - Asn1Sequence seq) - { - if (seq.Count < 2 || seq.Count > 3) - throw new ArgumentException("Wrong number of elements in sequence", "seq"); - - octStr = (Asn1OctetString)seq[0]; - iterationCount = (DerInteger)seq[1]; - - if (seq.Count > 2) - { - keyLength = (DerInteger)seq[2]; - } - } - - public Pbkdf2Params( - byte[] salt, - int iterationCount) - { - this.octStr = new DerOctetString(salt); - this.iterationCount = new DerInteger(iterationCount); - } + public class Pbkdf2Params + : Asn1Encodable + { + private static AlgorithmIdentifier algid_hmacWithSHA1 = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdHmacWithSha1, DerNull.Instance); + + private readonly Asn1OctetString octStr; + private readonly DerInteger iterationCount, keyLength; + private readonly AlgorithmIdentifier prf; + + public static Pbkdf2Params GetInstance( + object obj) + { + if (obj == null || obj is Pbkdf2Params) + return (Pbkdf2Params)obj; + + if (obj is Asn1Sequence) + return new Pbkdf2Params((Asn1Sequence)obj); + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public Pbkdf2Params( + Asn1Sequence seq) + { + if (seq.Count < 2 || seq.Count > 4) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.octStr = (Asn1OctetString)seq[0]; + this.iterationCount = (DerInteger)seq[1]; + + Asn1Encodable kl = null, d = null; + if (seq.Count > 3) + { + kl = seq[2]; + d = seq[3]; + } + else if (seq.Count > 2) + { + if (seq[2] is DerInteger) + { + kl = seq[2]; + } + else + { + d = seq[2]; + } + } + if (kl != null) + { + keyLength = (DerInteger)kl; + } + if (d != null) + { + prf = AlgorithmIdentifier.GetInstance(d); + } + } + + public Pbkdf2Params( + byte[] salt, + int iterationCount) + { + this.octStr = new DerOctetString(salt); + this.iterationCount = new DerInteger(iterationCount); + } public Pbkdf2Params( byte[] salt, @@ -55,32 +78,65 @@ namespace Org.BouncyCastle.Asn1.Pkcs this.keyLength = new DerInteger(keyLength); } - public byte[] GetSalt() - { - return octStr.GetOctets(); - } - - public BigInteger IterationCount - { - get { return iterationCount.Value; } - } - - public BigInteger KeyLength - { - get { return keyLength == null ? null : keyLength.Value; } - } - - public override Asn1Object ToAsn1Object() - { - Asn1EncodableVector v = new Asn1EncodableVector( - octStr, iterationCount); - - if (keyLength != null) - { - v.Add(keyLength); - } - - return new DerSequence(v); - } - } + public Pbkdf2Params( + byte[] salt, + int iterationCount, + int keyLength, + AlgorithmIdentifier prf) + : this(salt, iterationCount, keyLength) + { + this.prf = prf; + } + + public Pbkdf2Params( + byte[] salt, + int iterationCount, + AlgorithmIdentifier prf) + : this(salt, iterationCount) + { + this.prf = prf; + } + + public byte[] GetSalt() + { + return octStr.GetOctets(); + } + + public BigInteger IterationCount + { + get { return iterationCount.Value; } + } + + public BigInteger KeyLength + { + get { return keyLength == null ? null : keyLength.Value; } + } + + public bool IsDefaultPrf + { + get { return prf == null || prf.Equals(algid_hmacWithSHA1); } + } + + public AlgorithmIdentifier Prf + { + get { return prf != null ? prf : algid_hmacWithSHA1; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + octStr, iterationCount); + + if (keyLength != null) + { + v.Add(keyLength); + } + if (!IsDefaultPrf) + { + v.Add(prf); + } + + return new DerSequence(v); + } + } } diff --git a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
index 91b6fb456..404277ba6 100644 --- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs +++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
@@ -10,46 +10,46 @@ namespace Org.BouncyCastle.Asn1.Pkcs public class PrivateKeyInfo : Asn1Encodable { - private readonly Asn1Object privKey; + private readonly Asn1OctetString privKey; private readonly AlgorithmIdentifier algID; - private readonly Asn1Set attributes; + private readonly Asn1Set attributes; - public static PrivateKeyInfo GetInstance( - object obj) - { - if (obj is PrivateKeyInfo) - return (PrivateKeyInfo) obj; + public static PrivateKeyInfo GetInstance(Asn1TaggedObject obj, bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } - if (obj != null) - return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); + public static PrivateKeyInfo GetInstance( + object obj) + { + if (obj == null) + return null; + if (obj is PrivateKeyInfo) + return (PrivateKeyInfo) obj; + return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); + } - return null; - } + public PrivateKeyInfo(AlgorithmIdentifier algID, Asn1Object privateKey) + : this(algID, privateKey, null) + { + } - public PrivateKeyInfo( + public PrivateKeyInfo( AlgorithmIdentifier algID, - Asn1Object privateKey) - : this(algID, privateKey, null) - { - } - - public PrivateKeyInfo( - AlgorithmIdentifier algID, - Asn1Object privateKey, - Asn1Set attributes) - { - this.privKey = privateKey; - this.algID = algID; - this.attributes = attributes; - } - - private PrivateKeyInfo( - Asn1Sequence seq) + Asn1Object privateKey, + Asn1Set attributes) + { + this.algID = algID; + this.privKey = new DerOctetString(privateKey.GetEncoded(Asn1Encodable.Der)); + this.attributes = attributes; + } + + private PrivateKeyInfo(Asn1Sequence seq) { IEnumerator e = seq.GetEnumerator(); - e.MoveNext(); - BigInteger version = ((DerInteger) e.Current).Value; + e.MoveNext(); + BigInteger version = ((DerInteger)e.Current).Value; if (version.IntValue != 0) { throw new ArgumentException("wrong version for private key info: " + version.IntValue); @@ -57,41 +57,53 @@ namespace Org.BouncyCastle.Asn1.Pkcs e.MoveNext(); algID = AlgorithmIdentifier.GetInstance(e.Current); + e.MoveNext(); + privKey = Asn1OctetString.GetInstance(e.Current); - try - { - e.MoveNext(); - Asn1OctetString data = (Asn1OctetString) e.Current; - - privKey = Asn1Object.FromByteArray(data.GetOctets()); - } - catch (IOException) + if (e.MoveNext()) { - throw new ArgumentException("Error recoverying private key from sequence"); + attributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false); } + } - if (e.MoveNext()) - { - attributes = Asn1Set.GetInstance((Asn1TaggedObject) e.Current, false); - } - } + public virtual AlgorithmIdentifier PrivateKeyAlgorithm + { + get { return algID; } + } - public AlgorithmIdentifier AlgorithmID - { - get { return algID; } - } + [Obsolete("Use 'PrivateKeyAlgorithm' property instead")] + public virtual AlgorithmIdentifier AlgorithmID + { + get { return algID; } + } - public Asn1Object PrivateKey - { - get { return privKey; } - } + public virtual Asn1Object ParsePrivateKey() + { + return Asn1Object.FromByteArray(privKey.GetOctets()); + } - public Asn1Set Attributes - { - get { return attributes; } - } + [Obsolete("Use 'ParsePrivateKey' instead")] + public virtual Asn1Object PrivateKey + { + get + { + try + { + return ParsePrivateKey(); + } + catch (IOException) + { + throw new InvalidOperationException("unable to parse private key"); + } + } + } - /** + public virtual Asn1Set Attributes + { + get { return attributes; } + } + + /** * write out an RSA private key with its associated information * as described in Pkcs8. * <pre> @@ -110,17 +122,14 @@ namespace Org.BouncyCastle.Asn1.Pkcs */ public override Asn1Object ToAsn1Object() { - Asn1EncodableVector v = new Asn1EncodableVector( - new DerInteger(0), - algID, - new DerOctetString(privKey)); + Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(0), algID, privKey); - if (attributes != null) - { - v.Add(new DerTaggedObject(false, 0, attributes)); - } + if (attributes != null) + { + v.Add(new DerTaggedObject(false, 0, attributes)); + } - return new DerSequence(v); + return new DerSequence(v); } } } diff --git a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
index dbb07c744..721299105 100644 --- a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs +++ b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
@@ -18,7 +18,21 @@ namespace Org.BouncyCastle.Asn1.Pkcs private readonly BigInteger exponent2; private readonly BigInteger coefficient; - public RsaPrivateKeyStructure( + public static RsaPrivateKeyStructure GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static RsaPrivateKeyStructure GetInstance(object obj) + { + if (obj == null) + return null; + if (obj is RsaPrivateKeyStructure) + return (RsaPrivateKeyStructure)obj; + return new RsaPrivateKeyStructure(Asn1Sequence.GetInstance(obj)); + } + + public RsaPrivateKeyStructure( BigInteger modulus, BigInteger publicExponent, BigInteger privateExponent, @@ -38,64 +52,65 @@ namespace Org.BouncyCastle.Asn1.Pkcs this.coefficient = coefficient; } - public RsaPrivateKeyStructure( + [Obsolete("Use 'GetInstance' method(s) instead")] + public RsaPrivateKeyStructure( Asn1Sequence seq) { - BigInteger version = ((DerInteger) seq[0]).Value; - if (version.IntValue != 0) + BigInteger version = ((DerInteger) seq[0]).Value; + if (version.IntValue != 0) throw new ArgumentException("wrong version for RSA private key"); - modulus = ((DerInteger) seq[1]).Value; - publicExponent = ((DerInteger) seq[2]).Value; - privateExponent = ((DerInteger) seq[3]).Value; - prime1 = ((DerInteger) seq[4]).Value; - prime2 = ((DerInteger) seq[5]).Value; - exponent1 = ((DerInteger) seq[6]).Value; - exponent2 = ((DerInteger) seq[7]).Value; - coefficient = ((DerInteger) seq[8]).Value; - } - - public BigInteger Modulus - { - get { return modulus; } - } - - public BigInteger PublicExponent - { - get { return publicExponent; } - } - - public BigInteger PrivateExponent - { - get { return privateExponent; } - } - - public BigInteger Prime1 - { - get { return prime1; } - } - - public BigInteger Prime2 - { - get { return prime2; } - } - - public BigInteger Exponent1 - { - get { return exponent1; } - } - - public BigInteger Exponent2 - { - get { return exponent2; } - } - - public BigInteger Coefficient - { - get { return coefficient; } - } - - /** + modulus = ((DerInteger) seq[1]).Value; + publicExponent = ((DerInteger) seq[2]).Value; + privateExponent = ((DerInteger) seq[3]).Value; + prime1 = ((DerInteger) seq[4]).Value; + prime2 = ((DerInteger) seq[5]).Value; + exponent1 = ((DerInteger) seq[6]).Value; + exponent2 = ((DerInteger) seq[7]).Value; + coefficient = ((DerInteger) seq[8]).Value; + } + + public BigInteger Modulus + { + get { return modulus; } + } + + public BigInteger PublicExponent + { + get { return publicExponent; } + } + + public BigInteger PrivateExponent + { + get { return privateExponent; } + } + + public BigInteger Prime1 + { + get { return prime1; } + } + + public BigInteger Prime2 + { + get { return prime2; } + } + + public BigInteger Exponent1 + { + get { return exponent1; } + } + + public BigInteger Exponent2 + { + get { return exponent2; } + } + + public BigInteger Coefficient + { + get { return coefficient; } + } + + /** * This outputs the key in Pkcs1v2 format. * <pre> * RsaPrivateKey ::= Sequence { @@ -116,16 +131,16 @@ namespace Org.BouncyCastle.Asn1.Pkcs */ public override Asn1Object ToAsn1Object() { - return new DerSequence( - new DerInteger(0), // version - new DerInteger(Modulus), - new DerInteger(PublicExponent), - new DerInteger(PrivateExponent), - new DerInteger(Prime1), - new DerInteger(Prime2), - new DerInteger(Exponent1), - new DerInteger(Exponent2), - new DerInteger(Coefficient)); + return new DerSequence( + new DerInteger(0), // version + new DerInteger(Modulus), + new DerInteger(PublicExponent), + new DerInteger(PrivateExponent), + new DerInteger(Prime1), + new DerInteger(Prime2), + new DerInteger(Exponent1), + new DerInteger(Exponent2), + new DerInteger(Coefficient)); } } } diff --git a/crypto/src/asn1/sec/ECPrivateKeyStructure.cs b/crypto/src/asn1/sec/ECPrivateKeyStructure.cs
index 2e9c27fd2..8d805fa30 100644 --- a/crypto/src/asn1/sec/ECPrivateKeyStructure.cs +++ b/crypto/src/asn1/sec/ECPrivateKeyStructure.cs
@@ -6,113 +6,121 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1.Sec { - /** - * the elliptic curve private key object from SEC 1 - */ - public class ECPrivateKeyStructure - : Asn1Encodable - { - private readonly Asn1Sequence seq; - - public ECPrivateKeyStructure( - Asn1Sequence seq) - { - if (seq == null) - throw new ArgumentNullException("seq"); - - this.seq = seq; - } - - public ECPrivateKeyStructure( - BigInteger key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this.seq = new DerSequence( - new DerInteger(1), - new DerOctetString(key.ToByteArrayUnsigned())); - } - - public ECPrivateKeyStructure( - BigInteger key, - Asn1Encodable parameters) - : this(key, null, parameters) - { - } - - public ECPrivateKeyStructure( - BigInteger key, - DerBitString publicKey, - Asn1Encodable parameters) - { - if (key == null) - throw new ArgumentNullException("key"); - - Asn1EncodableVector v = new Asn1EncodableVector( - new DerInteger(1), - new DerOctetString(key.ToByteArrayUnsigned())); - - if (parameters != null) - { - v.Add(new DerTaggedObject(true, 0, parameters)); - } - - if (publicKey != null) - { - v.Add(new DerTaggedObject(true, 1, publicKey)); - } - - this.seq = new DerSequence(v); - } - - public BigInteger GetKey() - { - Asn1OctetString octs = (Asn1OctetString) seq[1]; - - return new BigInteger(1, octs.GetOctets()); - } - - public DerBitString GetPublicKey() - { - return (DerBitString) GetObjectInTag(1); - } - - public Asn1Object GetParameters() - { - return GetObjectInTag(0); - } - - private Asn1Object GetObjectInTag( - int tagNo) - { - foreach (Asn1Encodable ae in seq) - { - Asn1Object obj = ae.ToAsn1Object(); - - if (obj is Asn1TaggedObject) - { - Asn1TaggedObject tag = (Asn1TaggedObject) obj; - if (tag.TagNo == tagNo) - { - return tag.GetObject(); - } - } - } - - return null; - } - - /** - * ECPrivateKey ::= SEQUENCE { - * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), - * privateKey OCTET STRING, - * parameters [0] Parameters OPTIONAL, - * publicKey [1] BIT STRING OPTIONAL } - */ - public override Asn1Object ToAsn1Object() - { - return seq; - } - } + /** + * the elliptic curve private key object from SEC 1 + */ + public class ECPrivateKeyStructure + : Asn1Encodable + { + private readonly Asn1Sequence seq; + + public static ECPrivateKeyStructure GetInstance(object obj) + { + if (obj == null) + return null; + if (obj is ECPrivateKeyStructure) + return (ECPrivateKeyStructure)obj; + return new ECPrivateKeyStructure(Asn1Sequence.GetInstance(obj)); + } + + public ECPrivateKeyStructure( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + + this.seq = seq; + } + + public ECPrivateKeyStructure( + BigInteger key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this.seq = new DerSequence( + new DerInteger(1), + new DerOctetString(key.ToByteArrayUnsigned())); + } + + public ECPrivateKeyStructure( + BigInteger key, + Asn1Encodable parameters) + : this(key, null, parameters) + { + } + + public ECPrivateKeyStructure( + BigInteger key, + DerBitString publicKey, + Asn1Encodable parameters) + { + if (key == null) + throw new ArgumentNullException("key"); + + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger(1), + new DerOctetString(key.ToByteArrayUnsigned())); + + if (parameters != null) + { + v.Add(new DerTaggedObject(true, 0, parameters)); + } + + if (publicKey != null) + { + v.Add(new DerTaggedObject(true, 1, publicKey)); + } + + this.seq = new DerSequence(v); + } + + public virtual BigInteger GetKey() + { + Asn1OctetString octs = (Asn1OctetString) seq[1]; + + return new BigInteger(1, octs.GetOctets()); + } + + public virtual DerBitString GetPublicKey() + { + return (DerBitString) GetObjectInTag(1); + } + + public virtual Asn1Object GetParameters() + { + return GetObjectInTag(0); + } + + private Asn1Object GetObjectInTag(int tagNo) + { + foreach (Asn1Encodable ae in seq) + { + Asn1Object obj = ae.ToAsn1Object(); + + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tag = (Asn1TaggedObject) obj; + if (tag.TagNo == tagNo) + { + return tag.GetObject(); + } + } + } + + return null; + } + + /** + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] Parameters OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL } + */ + public override Asn1Object ToAsn1Object() + { + return seq; + } + } } diff --git a/crypto/src/asn1/sec/SECNamedCurves.cs b/crypto/src/asn1/sec/SECNamedCurves.cs
index 7e2afbe6e..ca71a4e66 100644 --- a/crypto/src/asn1/sec/SECNamedCurves.cs +++ b/crypto/src/asn1/sec/SECNamedCurves.cs
@@ -54,9 +54,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "09487239995A5EE76B55F9C2F098")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "09487239995A5EE76B55F9C2F098" + "A89CE5AF8724C0A23E0E0FF77500")); @@ -85,9 +83,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "4BA30AB5E892B4E1649DD0928643")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "4BA30AB5E892B4E1649DD0928643" + "ADCD46F5882E3747DEF36E956E97")); @@ -116,9 +112,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "161FF7528B899B2D0C28607CA52C5B86")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "161FF7528B899B2D0C28607CA52C5B86" + "CF5AC8395BAFEB13C02DA292DDED7A83")); @@ -147,9 +141,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "7B6AA5D85E572983E6FB32A7CDEBC140")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "7B6AA5D85E572983E6FB32A7CDEBC140" + "27B6916A894D3AEE7106FE805FC34B44")); @@ -186,14 +178,12 @@ namespace Org.BouncyCastle.Asn1.Sec new BigInteger[]{ new BigInteger("127971af8721782ecffa3", 16), new BigInteger("9162fbe73984472a0a9e", 16) }, - new BigInteger("48b17df39cc22395054e8", 16), - new BigInteger("4b1a0f889c499de17a820", 16), - 163); + new BigInteger("9162fbe73984472a0a9d0590", 16), + new BigInteger("96341f1138933bc2f503fd44", 16), + 176); ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + "938CF935318FDCED6BC28286531733C3F03C4FEE")); @@ -222,9 +212,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "4A96B5688EF573284664698968C38BB913CBFC82")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "4A96B5688EF573284664698968C38BB913CBFC82" + "23A628553168947D59DCC912042351377AC5FB32")); @@ -253,9 +241,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "52DCB034293A117E1F4FF11B30F7199D3144CE6D")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "52DCB034293A117E1F4FF11B30F7199D3144CE6D" + "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E")); @@ -292,14 +278,12 @@ namespace Org.BouncyCastle.Asn1.Sec new BigInteger[]{ new BigInteger("12511cfe811d0f4e6bc688b4d", 16), new BigInteger("71169be7330b3038edb025f1", 16) }, - new BigInteger("1c45a6f9ccc2cc0e3b6c097c7", 16), - new BigInteger("2cfecd0037b1712b73ae19575", 16), - 194); + new BigInteger("71169be7330b3038edb025f1d0f9", 16), + new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16), + 208); ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D")); @@ -328,9 +312,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811")); @@ -367,14 +349,12 @@ namespace Org.BouncyCastle.Asn1.Sec new BigInteger[]{ new BigInteger("1243ae1b4d71613bc9f780a03690e", 16), new BigInteger("6b8cf07d4ca75c88957d9d670591", 16) }, - new BigInteger("35c6783ea653ae444abeceb382c82", 16), - new BigInteger("5c56f89bc5375b9a04fd364e31bdd", 16), - 227); + new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16), + new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16), + 240); ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C" + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5")); @@ -403,9 +383,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34")); @@ -442,14 +420,12 @@ namespace Org.BouncyCastle.Asn1.Sec new BigInteger[]{ new BigInteger("114ca50f7a8e2f3f657c1108d9d44cfd8", 16), new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16) }, - new BigInteger("c21b48869f51af37a1b243924a13ac55", 16), - new BigInteger("3910dfb58043a20a1bd51fea42aff9311", 16), - 258); + new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16), + new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16), + 272); ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")); @@ -478,9 +454,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5")); @@ -509,9 +483,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7" + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F")); @@ -540,9 +512,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.One; ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66" + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650")); @@ -572,9 +542,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "009D73616F35F4AB1407D73562C10F")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "009D73616F35F4AB1407D73562C10F" + "00A52830277958EE84D1315ED31886")); @@ -604,9 +572,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "01A57A6A7B26CA5EF52FCDB8164797")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "01A57A6A7B26CA5EF52FCDB8164797" + "00B3ADC94ED1FE674C06E695BABA1D")); @@ -638,9 +604,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "0081BAF91FDF9833C40F9C181343638399")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "0081BAF91FDF9833C40F9C181343638399" + "078C6E7EA38C001F73C8134B1B4EF9E150")); @@ -672,9 +636,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "0356DCD8F2F95031AD652D23951BB366A8")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "0356DCD8F2F95031AD652D23951BB366A8" + "0648F06D867940A5366D9E265DE9EB240F")); @@ -706,9 +668,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8" + "0289070FB05D38FF58321F2E800536D538CCDAA3D9")); @@ -740,9 +700,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "0369979697AB43897789566789567F787A7876A654")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "0369979697AB43897789566789567F787A7876A654" + "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883")); @@ -774,9 +732,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "03F0EBA16286A2D57EA0991168D4994637E8343E36")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "03F0EBA16286A2D57EA0991168D4994637E8343E36" + "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1")); @@ -806,9 +762,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1" + "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05")); @@ -838,9 +792,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F" + "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C")); @@ -870,9 +822,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126" + "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3")); @@ -902,9 +852,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B" + "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052")); @@ -934,9 +882,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC" + "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA")); @@ -968,9 +914,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836" + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259")); @@ -1002,9 +946,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053" + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4")); @@ -1034,9 +976,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746" + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B")); @@ -1066,9 +1006,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7" + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706")); @@ -1100,9 +1038,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(4); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("02" - //+ "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972" + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3")); @@ -1134,9 +1070,7 @@ namespace Org.BouncyCastle.Asn1.Sec BigInteger h = BigInteger.ValueOf(2); ECCurve curve = new F2mCurve(m, k1, k2, k3, a, b, n, h); - //ECPoint G = curve.DecodePoint(Hex.Decode("03" - //+ "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19")); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19" + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B")); @@ -1154,7 +1088,7 @@ namespace Org.BouncyCastle.Asn1.Sec DerObjectIdentifier oid, X9ECParametersHolder holder) { - objIds.Add(name, oid); + objIds.Add(Platform.ToLowerInvariant(name), oid); names.Add(oid, name); curves.Add(oid, holder); } @@ -1200,9 +1134,7 @@ namespace Org.BouncyCastle.Asn1.Sec public static X9ECParameters GetByName( string name) { - DerObjectIdentifier oid = (DerObjectIdentifier) - objIds[Platform.ToLowerInvariant(name)]; - + DerObjectIdentifier oid = GetOid(name); return oid == null ? null : GetByOid(oid); } @@ -1215,8 +1147,7 @@ namespace Org.BouncyCastle.Asn1.Sec public static X9ECParameters GetByOid( DerObjectIdentifier oid) { - X9ECParametersHolder holder = (X9ECParametersHolder) curves[oid]; - + X9ECParametersHolder holder = (X9ECParametersHolder)curves[oid]; return holder == null ? null : holder.Parameters; } @@ -1238,7 +1169,7 @@ namespace Org.BouncyCastle.Asn1.Sec public static string GetName( DerObjectIdentifier oid) { - return (string) names[oid]; + return (string)names[oid]; } /** @@ -1247,7 +1178,7 @@ namespace Org.BouncyCastle.Asn1.Sec */ public static IEnumerable Names { - get { return new EnumerableProxy(objIds.Keys); } + get { return new EnumerableProxy(names.Values); } } } } diff --git a/crypto/src/asn1/smime/SMIMECapabilities.cs b/crypto/src/asn1/smime/SMIMECapabilities.cs
index ca3c3af7d..5fb67dde1 100644 --- a/crypto/src/asn1/smime/SMIMECapabilities.cs +++ b/crypto/src/asn1/smime/SMIMECapabilities.cs
@@ -2,6 +2,7 @@ using System; using System.Collections; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; @@ -25,6 +26,11 @@ namespace Org.BouncyCastle.Asn1.Smime /** * encryption algorithms preferences */ + public static readonly DerObjectIdentifier Aes256Cbc = NistObjectIdentifiers.IdAes256Cbc; + public static readonly DerObjectIdentifier Aes192Cbc = NistObjectIdentifiers.IdAes192Cbc; + public static readonly DerObjectIdentifier Aes128Cbc = NistObjectIdentifiers.IdAes128Cbc; + public static readonly DerObjectIdentifier IdeaCbc = new DerObjectIdentifier("1.3.6.1.4.1.188.7.1.1.2"); + public static readonly DerObjectIdentifier Cast5Cbc = new DerObjectIdentifier("1.2.840.113533.7.66.10"); public static readonly DerObjectIdentifier DesCbc = new DerObjectIdentifier("1.3.14.3.2.7"); public static readonly DerObjectIdentifier DesEde3Cbc = PkcsObjectIdentifiers.DesEde3Cbc; public static readonly DerObjectIdentifier RC2Cbc = PkcsObjectIdentifiers.RC2Cbc; diff --git a/crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs b/crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs
index 05060c109..ba3eda620 100644 --- a/crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs +++ b/crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs
@@ -40,7 +40,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("04BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC31667CB477A1A8EC338F94741669C976316DA6321")), // G + new X9ECPoint(curve, Hex.Decode("04BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC31667CB477A1A8EC338F94741669C976316DA6321")), // G n, h); } } @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("04B199B13B9B34EFC1397E64BAEB05ACC265FF2378ADD6718B7C7C1961F0991B842443772152C9E0AD")), // G + new X9ECPoint(curve, Hex.Decode("04B199B13B9B34EFC1397E64BAEB05ACC265FF2378ADD6718B7C7C1961F0991B842443772152C9E0AD")), // G n, h); } } @@ -91,7 +91,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("04C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD614B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F")), // G + new X9ECPoint(curve, Hex.Decode("04C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD614B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F")), // G n, h); } } @@ -117,7 +117,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("043AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9")), // G' + new X9ECPoint(curve, Hex.Decode("043AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9")), // G' n, h); } } @@ -142,7 +142,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD")), // G + new X9ECPoint(curve, Hex.Decode("040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD")), // G n, h); } } @@ -168,7 +168,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("046AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D5800374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C")), // G' + new X9ECPoint(curve, Hex.Decode("046AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D5800374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C")), // G' n, h); } } @@ -193,7 +193,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")), // G + new X9ECPoint(curve, Hex.Decode("048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")), // G n, h); } } @@ -219,7 +219,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("04A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE")), // G' + new X9ECPoint(curve, Hex.Decode("04A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE")), // G' n, h); } } @@ -244,7 +244,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("0443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1")), // G + new X9ECPoint(curve, Hex.Decode("0443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1")), // G n, h); } } @@ -270,7 +270,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("04925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF3357F624A21BED5263BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B1B9BC0455FB0D2C3")), // G' + new X9ECPoint(curve, Hex.Decode("04925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF3357F624A21BED5263BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B1B9BC0455FB0D2C3")), // G' n, h); } } @@ -295,7 +295,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315")), // G + new X9ECPoint(curve, Hex.Decode("041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315")), // G n, h); } } @@ -321,7 +321,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("0418DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928")), // G' + new X9ECPoint(curve, Hex.Decode("0418DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928")), // G' n, h); } } @@ -346,7 +346,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("0481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892")), // G + new X9ECPoint(curve, Hex.Decode("0481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892")), // G n, h); } } @@ -372,7 +372,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust return new X9ECParameters( curve, - curve.DecodePoint(Hex.Decode("04640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332")), // G' + new X9ECPoint(curve, Hex.Decode("04640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332")), // G' n, h); } } @@ -387,35 +387,33 @@ namespace Org.BouncyCastle.Asn1.TeleTrust DerObjectIdentifier oid, X9ECParametersHolder holder) { - objIds.Add(name, oid); + objIds.Add(Platform.ToLowerInvariant(name), oid); names.Add(oid, name); curves.Add(oid, holder); } static TeleTrusTNamedCurves() { - DefineCurve("brainpoolp160r1", TeleTrusTObjectIdentifiers.BrainpoolP160R1, BrainpoolP160r1Holder.Instance); - DefineCurve("brainpoolp160t1", TeleTrusTObjectIdentifiers.BrainpoolP160T1, BrainpoolP160t1Holder.Instance); - DefineCurve("brainpoolp192r1", TeleTrusTObjectIdentifiers.BrainpoolP192R1, BrainpoolP192r1Holder.Instance); - DefineCurve("brainpoolp192t1", TeleTrusTObjectIdentifiers.BrainpoolP192T1, BrainpoolP192t1Holder.Instance); - DefineCurve("brainpoolp224r1", TeleTrusTObjectIdentifiers.BrainpoolP224R1, BrainpoolP224r1Holder.Instance); - DefineCurve("brainpoolp224t1", TeleTrusTObjectIdentifiers.BrainpoolP224T1, BrainpoolP224t1Holder.Instance); - DefineCurve("brainpoolp256r1", TeleTrusTObjectIdentifiers.BrainpoolP256R1, BrainpoolP256r1Holder.Instance); - DefineCurve("brainpoolp256t1", TeleTrusTObjectIdentifiers.BrainpoolP256T1, BrainpoolP256t1Holder.Instance); - DefineCurve("brainpoolp320r1", TeleTrusTObjectIdentifiers.BrainpoolP320R1, BrainpoolP320r1Holder.Instance); - DefineCurve("brainpoolp320t1", TeleTrusTObjectIdentifiers.BrainpoolP320T1, BrainpoolP320t1Holder.Instance); - DefineCurve("brainpoolp384r1", TeleTrusTObjectIdentifiers.BrainpoolP384R1, BrainpoolP384r1Holder.Instance); - DefineCurve("brainpoolp384t1", TeleTrusTObjectIdentifiers.BrainpoolP384T1, BrainpoolP384t1Holder.Instance); - DefineCurve("brainpoolp512r1", TeleTrusTObjectIdentifiers.BrainpoolP512R1, BrainpoolP512r1Holder.Instance); - DefineCurve("brainpoolp512t1", TeleTrusTObjectIdentifiers.BrainpoolP512T1, BrainpoolP512t1Holder.Instance); + DefineCurve("brainpoolP160r1", TeleTrusTObjectIdentifiers.BrainpoolP160R1, BrainpoolP160r1Holder.Instance); + DefineCurve("brainpoolP160t1", TeleTrusTObjectIdentifiers.BrainpoolP160T1, BrainpoolP160t1Holder.Instance); + DefineCurve("brainpoolP192r1", TeleTrusTObjectIdentifiers.BrainpoolP192R1, BrainpoolP192r1Holder.Instance); + DefineCurve("brainpoolP192t1", TeleTrusTObjectIdentifiers.BrainpoolP192T1, BrainpoolP192t1Holder.Instance); + DefineCurve("brainpoolP224r1", TeleTrusTObjectIdentifiers.BrainpoolP224R1, BrainpoolP224r1Holder.Instance); + DefineCurve("brainpoolP224t1", TeleTrusTObjectIdentifiers.BrainpoolP224T1, BrainpoolP224t1Holder.Instance); + DefineCurve("brainpoolP256r1", TeleTrusTObjectIdentifiers.BrainpoolP256R1, BrainpoolP256r1Holder.Instance); + DefineCurve("brainpoolP256t1", TeleTrusTObjectIdentifiers.BrainpoolP256T1, BrainpoolP256t1Holder.Instance); + DefineCurve("brainpoolP320r1", TeleTrusTObjectIdentifiers.BrainpoolP320R1, BrainpoolP320r1Holder.Instance); + DefineCurve("brainpoolP320t1", TeleTrusTObjectIdentifiers.BrainpoolP320T1, BrainpoolP320t1Holder.Instance); + DefineCurve("brainpoolP384r1", TeleTrusTObjectIdentifiers.BrainpoolP384R1, BrainpoolP384r1Holder.Instance); + DefineCurve("brainpoolP384t1", TeleTrusTObjectIdentifiers.BrainpoolP384T1, BrainpoolP384t1Holder.Instance); + DefineCurve("brainpoolP512r1", TeleTrusTObjectIdentifiers.BrainpoolP512R1, BrainpoolP512r1Holder.Instance); + DefineCurve("brainpoolP512t1", TeleTrusTObjectIdentifiers.BrainpoolP512T1, BrainpoolP512t1Holder.Instance); } public static X9ECParameters GetByName( string name) { - DerObjectIdentifier oid = (DerObjectIdentifier) - objIds[Platform.ToLowerInvariant(name)]; - + DerObjectIdentifier oid = GetOid(name); return oid == null ? null : GetByOid(oid); } @@ -428,8 +426,7 @@ namespace Org.BouncyCastle.Asn1.TeleTrust public static X9ECParameters GetByOid( DerObjectIdentifier oid) { - X9ECParametersHolder holder = (X9ECParametersHolder) curves[oid]; - + X9ECParametersHolder holder = (X9ECParametersHolder)curves[oid]; return holder == null ? null : holder.Parameters; } @@ -451,17 +448,16 @@ namespace Org.BouncyCastle.Asn1.TeleTrust public static string GetName( DerObjectIdentifier oid) { - return (string) names[oid]; + return (string)names[oid]; } - /** * returns an enumeration containing the name strings for curves * contained in this structure. */ public static IEnumerable Names { - get { return new EnumerableProxy(objIds.Keys); } + get { return new EnumerableProxy(names.Values); } } public static DerObjectIdentifier GetOid( diff --git a/crypto/src/asn1/util/FilterStream.cs b/crypto/src/asn1/util/FilterStream.cs
index 2b0494b78..980e7f176 100644 --- a/crypto/src/asn1/util/FilterStream.cs +++ b/crypto/src/asn1/util/FilterStream.cs
@@ -3,8 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Asn1.Utilities { + [Obsolete("Use Org.BouncyCastle.Utilities.IO.FilterStream")] public class FilterStream : Stream { + [Obsolete("Use Org.BouncyCastle.Utilities.IO.FilterStream")] public FilterStream(Stream s) { this.s = s; diff --git a/crypto/src/asn1/x509/AlgorithmIdentifier.cs b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
index 4ed3a400d..c6f4af5bf 100644 --- a/crypto/src/asn1/x509/AlgorithmIdentifier.cs +++ b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
@@ -69,11 +69,22 @@ namespace Org.BouncyCastle.Asn1.X509 } } + /// <summary> + /// Return the OID in the Algorithm entry of this identifier. + /// </summary> + public virtual DerObjectIdentifier Algorithm + { + get { return objectID; } + } + public virtual DerObjectIdentifier ObjectID { get { return objectID; } } + /// <summary> + /// Return the parameters structure in the Parameters entry of this identifier. + /// </summary> public Asn1Encodable Parameters { get { return parameters; } diff --git a/crypto/src/asn1/x509/ExtendedKeyUsage.cs b/crypto/src/asn1/x509/ExtendedKeyUsage.cs
index b5e4b7f8d..a5b11f210 100644 --- a/crypto/src/asn1/x509/ExtendedKeyUsage.cs +++ b/crypto/src/asn1/x509/ExtendedKeyUsage.cs
@@ -17,14 +17,14 @@ namespace Org.BouncyCastle.Asn1.X509 internal readonly IDictionary usageTable = Platform.CreateHashtable(); internal readonly Asn1Sequence seq; - public static ExtendedKeyUsage GetInstance( + public static ExtendedKeyUsage GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - public static ExtendedKeyUsage GetInstance( + public static ExtendedKeyUsage GetInstance( object obj) { if (obj is ExtendedKeyUsage) @@ -32,43 +32,43 @@ namespace Org.BouncyCastle.Asn1.X509 return (ExtendedKeyUsage) obj; } - if (obj is Asn1Sequence) + if (obj is Asn1Sequence) { return new ExtendedKeyUsage((Asn1Sequence) obj); } - if (obj is X509Extension) - { - return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); - } + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } - throw new ArgumentException("Invalid ExtendedKeyUsage: " + obj.GetType().Name); + throw new ArgumentException("Invalid ExtendedKeyUsage: " + obj.GetType().Name); } - private ExtendedKeyUsage( + private ExtendedKeyUsage( Asn1Sequence seq) { this.seq = seq; - foreach (object o in seq) - { - if (!(o is DerObjectIdentifier)) - throw new ArgumentException("Only DerObjectIdentifier instances allowed in ExtendedKeyUsage."); + foreach (object o in seq) + { + if (!(o is DerObjectIdentifier)) + throw new ArgumentException("Only DerObjectIdentifier instances allowed in ExtendedKeyUsage."); - this.usageTable.Add(o, o); + this.usageTable[o] = o; } } - public ExtendedKeyUsage( - params KeyPurposeID[] usages) - { - this.seq = new DerSequence(usages); + public ExtendedKeyUsage( + params KeyPurposeID[] usages) + { + this.seq = new DerSequence(usages); - foreach (KeyPurposeID usage in usages) - { - this.usageTable.Add(usage, usage); - } - } + foreach (KeyPurposeID usage in usages) + { + this.usageTable[usage] = usage; + } + } #if !SILVERLIGHT [Obsolete] @@ -84,20 +84,21 @@ namespace Org.BouncyCastle.Asn1.X509 { Asn1EncodableVector v = new Asn1EncodableVector(); - foreach (Asn1Object o in usages) + foreach (object usage in usages) { - v.Add(o); + Asn1Encodable o = KeyPurposeID.GetInstance(usage); - this.usageTable.Add(o, o); + v.Add(o); + this.usageTable[o] = o; } - this.seq = new DerSequence(v); + this.seq = new DerSequence(v); } - public bool HasKeyPurposeId( + public bool HasKeyPurposeId( KeyPurposeID keyPurposeId) { - return usageTable[keyPurposeId] != null; + return usageTable.Contains(keyPurposeId); } #if !SILVERLIGHT @@ -109,21 +110,21 @@ namespace Org.BouncyCastle.Asn1.X509 #endif /** - * Returns all extended key usages. - * The returned ArrayList contains DerObjectIdentifier instances. - * @return An ArrayList with all key purposes. - */ - public IList GetAllUsages() - { - return Platform.CreateArrayList(usageTable.Values); - } + * Returns all extended key usages. + * The returned ArrayList contains DerObjectIdentifier instances. + * @return An ArrayList with all key purposes. + */ + public IList GetAllUsages() + { + return Platform.CreateArrayList(usageTable.Values); + } public int Count - { - get { return usageTable.Count; } - } + { + get { return usageTable.Count; } + } - public override Asn1Object ToAsn1Object() + public override Asn1Object ToAsn1Object() { return seq; } diff --git a/crypto/src/asn1/x9/ECNamedCurveTable.cs b/crypto/src/asn1/x9/ECNamedCurveTable.cs
index 0030d376b..92d4393a8 100644 --- a/crypto/src/asn1/x9/ECNamedCurveTable.cs +++ b/crypto/src/asn1/x9/ECNamedCurveTable.cs
@@ -1,6 +1,7 @@ using System; using System.Collections; +using Org.BouncyCastle.Asn1.Anssi; using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.TeleTrust; @@ -32,17 +33,44 @@ namespace Org.BouncyCastle.Asn1.X9 if (ecP == null) { + ecP = NistNamedCurves.GetByName(name); + } + + if (ecP == null) + { ecP = TeleTrusTNamedCurves.GetByName(name); } if (ecP == null) { - ecP = NistNamedCurves.GetByName(name); + ecP = AnssiNamedCurves.GetByName(name); } return ecP; } + public static string GetName(DerObjectIdentifier oid) + { + string name = X962NamedCurves.GetName(oid); + if (name == null) + { + name = SecNamedCurves.GetName(oid); + } + if (name == null) + { + name = NistNamedCurves.GetName(oid); + } + if (name == null) + { + name = TeleTrusTNamedCurves.GetName(oid); + } + if (name == null) + { + name = AnssiNamedCurves.GetName(oid); + } + return name; + } + /** * return the object identifier signified by the passed in name. Null * if there is no object identifier associated with name. @@ -60,12 +88,17 @@ namespace Org.BouncyCastle.Asn1.X9 if (oid == null) { + oid = NistNamedCurves.GetOid(name); + } + + if (oid == null) + { oid = TeleTrusTNamedCurves.GetOid(name); } if (oid == null) { - oid = NistNamedCurves.GetOid(name); + oid = AnssiNamedCurves.GetOid(name); } return oid; @@ -87,12 +120,17 @@ namespace Org.BouncyCastle.Asn1.X9 ecP = SecNamedCurves.GetByOid(oid); } + // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup + if (ecP == null) { ecP = TeleTrusTNamedCurves.GetByOid(oid); } - // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup + if (ecP == null) + { + ecP = AnssiNamedCurves.GetByOid(oid); + } return ecP; } @@ -111,6 +149,7 @@ namespace Org.BouncyCastle.Asn1.X9 CollectionUtilities.AddRange(v, SecNamedCurves.Names); CollectionUtilities.AddRange(v, NistNamedCurves.Names); CollectionUtilities.AddRange(v, TeleTrusTNamedCurves.Names); + CollectionUtilities.AddRange(v, AnssiNamedCurves.Names); return v; } } diff --git a/crypto/src/asn1/x9/X962NamedCurves.cs b/crypto/src/asn1/x9/X962NamedCurves.cs
index 6b76c4eb4..6fa4e7c4b 100644 --- a/crypto/src/asn1/x9/X962NamedCurves.cs +++ b/crypto/src/asn1/x9/X962NamedCurves.cs
@@ -38,7 +38,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp192v1, - cFp192v1.DecodePoint( + new X9ECPoint(cFp192v1, Hex.Decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), n, h, Hex.Decode("3045AE6FC8422f64ED579528D38120EAE12196D5")); @@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp192v2, - cFp192v2.DecodePoint( + new X9ECPoint(cFp192v2, Hex.Decode("03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a")), n, h, Hex.Decode("31a92ee2029fd10d901b113e990710f0d21ac6b6")); @@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp192v3, - cFp192v3.DecodePoint( + new X9ECPoint(cFp192v3, Hex.Decode("027d29778100c65a1da1783716588dce2b8b4aee8e228f1896")), n, h, Hex.Decode("c469684435deb378c4b65ca9591e2a5763059a2e")); @@ -119,7 +119,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp239v1, - cFp239v1.DecodePoint( + new X9ECPoint(cFp239v1, Hex.Decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), n, h, Hex.Decode("e43bb460f0b80cc0c0b075798e948060f8321b7d")); @@ -146,7 +146,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp239v2, - cFp239v2.DecodePoint( + new X9ECPoint(cFp239v2, Hex.Decode("0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7")), n, h, Hex.Decode("e8b4011604095303ca3b8099982be09fcb9ae616")); @@ -173,7 +173,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp239v3, - cFp239v3.DecodePoint( + new X9ECPoint(cFp239v3, Hex.Decode("036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a")), n, h, Hex.Decode("7d7374168ffe3471b60a857686a19475d3bfa2ff")); @@ -200,7 +200,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( cFp256v1, - cFp256v1.DecodePoint( + new X9ECPoint(cFp256v1, Hex.Decode("036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")), n, h, Hex.Decode("c49d360886e704936a6678e1139d26b7819f7e90")); @@ -231,7 +231,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m163v1, - c2m163v1.DecodePoint( + new X9ECPoint(c2m163v1, Hex.Decode("0307AF69989546103D79329FCC3D74880F33BBE803CB")), n, h, Hex.Decode("D2C0FB15760860DEF1EEF4D696E6768756151754")); @@ -259,7 +259,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m163v2, - c2m163v2.DecodePoint( + new X9ECPoint(c2m163v2, Hex.Decode("030024266E4EB5106D0A964D92C4860E2671DB9B6CC5")), n, h, null); @@ -287,7 +287,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m163v3, - c2m163v3.DecodePoint(Hex.Decode("0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB")), + new X9ECPoint(c2m163v3, Hex.Decode("0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB")), n, h, null); } @@ -314,7 +314,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m176w1, - c2m176w1.DecodePoint( + new X9ECPoint(c2m176w1, Hex.Decode("038D16C2866798B600F9F08BB4A8E860F3298CE04A5798")), n, h, null); @@ -342,7 +342,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m191v1, - c2m191v1.DecodePoint( + new X9ECPoint(c2m191v1, Hex.Decode("0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D")), n, h, Hex.Decode("4E13CA542744D696E67687561517552F279A8C84")); @@ -370,7 +370,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m191v2, - c2m191v2.DecodePoint( + new X9ECPoint(c2m191v2, Hex.Decode("023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10")), n, h, null); @@ -398,7 +398,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m191v3, - c2m191v3.DecodePoint( + new X9ECPoint(c2m191v3, Hex.Decode("03375D4CE24FDE434489DE8746E71786015009E66E38A926DD")), n, h, null); @@ -426,7 +426,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m208w1, - c2m208w1.DecodePoint( + new X9ECPoint(c2m208w1, Hex.Decode("0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A")), n, h, null); @@ -454,7 +454,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m239v1, - c2m239v1.DecodePoint( + new X9ECPoint(c2m239v1, Hex.Decode("0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D")), n, h, null); @@ -482,7 +482,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m239v2, - c2m239v2.DecodePoint( + new X9ECPoint(c2m239v2, Hex.Decode("0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205")), n, h, null); @@ -510,7 +510,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m239v3, - c2m239v3.DecodePoint( + new X9ECPoint(c2m239v3, Hex.Decode("0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92")), n, h, null); @@ -538,7 +538,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m272w1, - c2m272w1.DecodePoint( + new X9ECPoint(c2m272w1, Hex.Decode("026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D")), n, h, null); @@ -566,7 +566,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m304w1, - c2m304w1.DecodePoint( + new X9ECPoint(c2m304w1, Hex.Decode("02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614")), n, h, null); @@ -594,7 +594,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m359v1, - c2m359v1.DecodePoint( + new X9ECPoint(c2m359v1, Hex.Decode("033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097")), n, h, null); @@ -622,7 +622,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m368w1, - c2m368w1.DecodePoint( + new X9ECPoint(c2m368w1, Hex.Decode("021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F")), n, h, null); @@ -650,7 +650,7 @@ namespace Org.BouncyCastle.Asn1.X9 return new X9ECParameters( c2m431r1, - c2m431r1.DecodePoint( + new X9ECPoint(c2m431r1, Hex.Decode("02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7")), n, h, null); @@ -666,7 +666,7 @@ namespace Org.BouncyCastle.Asn1.X9 DerObjectIdentifier oid, X9ECParametersHolder holder) { - objIds.Add(name, oid); + objIds.Add(Platform.ToLowerInvariant(name), oid); names.Add(oid, name); curves.Add(oid, holder); } @@ -701,8 +701,7 @@ namespace Org.BouncyCastle.Asn1.X9 public static X9ECParameters GetByName( string name) { - DerObjectIdentifier oid = (DerObjectIdentifier)objIds[Platform.ToLowerInvariant(name)]; - + DerObjectIdentifier oid = GetOid(name); return oid == null ? null : GetByOid(oid); } @@ -715,8 +714,7 @@ namespace Org.BouncyCastle.Asn1.X9 public static X9ECParameters GetByOid( DerObjectIdentifier oid) { - X9ECParametersHolder holder = (X9ECParametersHolder) curves[oid]; - + X9ECParametersHolder holder = (X9ECParametersHolder)curves[oid]; return holder == null ? null : holder.Parameters; } @@ -738,7 +736,7 @@ namespace Org.BouncyCastle.Asn1.X9 public static string GetName( DerObjectIdentifier oid) { - return (string) names[oid]; + return (string)names[oid]; } /** @@ -747,7 +745,7 @@ namespace Org.BouncyCastle.Asn1.X9 */ public static IEnumerable Names { - get { return new EnumerableProxy(objIds.Keys); } + get { return new EnumerableProxy(names.Values); } } } } diff --git a/crypto/src/asn1/x9/X9ECParameters.cs b/crypto/src/asn1/x9/X9ECParameters.cs
index a192e4c52..2b6b14bcb 100644 --- a/crypto/src/asn1/x9/X9ECParameters.cs +++ b/crypto/src/asn1/x9/X9ECParameters.cs
@@ -15,7 +15,7 @@ namespace Org.BouncyCastle.Asn1.X9 { private X9FieldID fieldID; private ECCurve curve; - private ECPoint g; + private X9ECPoint g; private BigInteger n; private BigInteger h; private byte[] seed; @@ -29,36 +29,28 @@ namespace Org.BouncyCastle.Asn1.X9 throw new ArgumentException("bad version in X9ECParameters"); } - X9Curve x9c = null; - if (seq[2] is X9Curve) - { - x9c = (X9Curve) seq[2]; - } - else - { - x9c = new X9Curve( - new X9FieldID( - (Asn1Sequence) seq[1]), - (Asn1Sequence) seq[2]); - } + X9Curve x9c = new X9Curve( + X9FieldID.GetInstance(seq[1]), + Asn1Sequence.GetInstance(seq[2])); this.curve = x9c.Curve; + object p = seq[3]; - if (seq[3] is X9ECPoint) + if (p is X9ECPoint) { - this.g = ((X9ECPoint) seq[3]).Point; + this.g = ((X9ECPoint)p); } else { - this.g = new X9ECPoint(curve, (Asn1OctetString) seq[3]).Point; + this.g = new X9ECPoint(curve, (Asn1OctetString)p); } - this.n = ((DerInteger) seq[4]).Value; + this.n = ((DerInteger)seq[4]).Value; this.seed = x9c.GetSeed(); if (seq.Count == 6) { - this.h = ((DerInteger) seq[5]).Value; + this.h = ((DerInteger)seq[5]).Value; } } @@ -66,7 +58,16 @@ namespace Org.BouncyCastle.Asn1.X9 ECCurve curve, ECPoint g, BigInteger n) - : this(curve, g, n, BigInteger.One, null) + : this(curve, g, n, null, null) + { + } + + public X9ECParameters( + ECCurve curve, + X9ECPoint g, + BigInteger n, + BigInteger h) + : this(curve, g, n, h, null) { } @@ -85,9 +86,19 @@ namespace Org.BouncyCastle.Asn1.X9 BigInteger n, BigInteger h, byte[] seed) + : this(curve, new X9ECPoint(g), n, h, seed) + { + } + + public X9ECParameters( + ECCurve curve, + X9ECPoint g, + BigInteger n, + BigInteger h, + byte[] seed) { this.curve = curve; - this.g = g.Normalize(); + this.g = g; this.n = n; this.h = h; this.seed = seed; @@ -126,7 +137,7 @@ namespace Org.BouncyCastle.Asn1.X9 public ECPoint G { - get { return g; } + get { return g.Point; } } public BigInteger N @@ -136,16 +147,7 @@ namespace Org.BouncyCastle.Asn1.X9 public BigInteger H { - get - { - if (h == null) - { - // TODO - this should be calculated, it will cause issues with custom curves. - return BigInteger.One; - } - - return h; - } + get { return h; } } public byte[] GetSeed() @@ -154,6 +156,36 @@ namespace Org.BouncyCastle.Asn1.X9 } /** + * Return the ASN.1 entry representing the Curve. + * + * @return the X9Curve for the curve in these parameters. + */ + public X9Curve CurveEntry + { + get { return new X9Curve(curve, seed); } + } + + /** + * Return the ASN.1 entry representing the FieldID. + * + * @return the X9FieldID for the FieldID in these parameters. + */ + public X9FieldID FieldIDEntry + { + get { return fieldID; } + } + + /** + * Return the ASN.1 entry representing the base point G. + * + * @return the X9ECPoint for the base point in these parameters. + */ + public X9ECPoint BaseEntry + { + get { return g; } + } + + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * ECParameters ::= Sequence { @@ -169,10 +201,10 @@ namespace Org.BouncyCastle.Asn1.X9 public override Asn1Object ToAsn1Object() { Asn1EncodableVector v = new Asn1EncodableVector( - new DerInteger(1), + new DerInteger(BigInteger.One), fieldID, new X9Curve(curve, seed), - new X9ECPoint(g), + g, new DerInteger(n)); if (h != null) diff --git a/crypto/src/asn1/x9/X9ECParametersHolder.cs b/crypto/src/asn1/x9/X9ECParametersHolder.cs
index b3455709c..e802b738c 100644 --- a/crypto/src/asn1/x9/X9ECParametersHolder.cs +++ b/crypto/src/asn1/x9/X9ECParametersHolder.cs
@@ -8,14 +8,17 @@ namespace Org.BouncyCastle.Asn1.X9 { get { - if (parameters == null) - { - parameters = CreateParameters(); - } + lock (this) + { + if (parameters == null) + { + parameters = CreateParameters(); + } - return parameters; - } - } + return parameters; + } + } + } protected abstract X9ECParameters CreateParameters(); } diff --git a/crypto/src/asn1/x9/X9ECPoint.cs b/crypto/src/asn1/x9/X9ECPoint.cs
index 75d58cd38..7ef4f13bc 100644 --- a/crypto/src/asn1/x9/X9ECPoint.cs +++ b/crypto/src/asn1/x9/X9ECPoint.cs
@@ -1,5 +1,7 @@ using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Asn1.X9 { /** @@ -8,24 +10,58 @@ namespace Org.BouncyCastle.Asn1.X9 public class X9ECPoint : Asn1Encodable { - private readonly ECPoint p; + private readonly Asn1OctetString encoding; + + private ECCurve c; + private ECPoint p; + + public X9ECPoint(ECPoint p) + : this(p, false) + { + } - public X9ECPoint( - ECPoint p) + public X9ECPoint(ECPoint p, bool compressed) { this.p = p.Normalize(); + this.encoding = new DerOctetString(p.GetEncoded(compressed)); + } + + public X9ECPoint(ECCurve c, byte[] encoding) + { + this.c = c; + this.encoding = new DerOctetString(Arrays.Clone(encoding)); } - public X9ECPoint( - ECCurve c, - Asn1OctetString s) + public X9ECPoint(ECCurve c, Asn1OctetString s) + : this(c, s.GetOctets()) { - this.p = c.DecodePoint(s.GetOctets()); + } + + public byte[] GetPointEncoding() + { + return Arrays.Clone(encoding.GetOctets()); } public ECPoint Point { - get { return p; } + get + { + if (p == null) + { + p = c.DecodePoint(encoding.GetOctets()).Normalize(); + } + + return p; + } + } + + public bool IsPointCompressed + { + get + { + byte[] octets = encoding.GetOctets(); + return octets != null && octets.Length > 0 && (octets[0] == 2 || octets[0] == 3); + } } /** @@ -38,7 +74,7 @@ namespace Org.BouncyCastle.Asn1.X9 */ public override Asn1Object ToAsn1Object() { - return new DerOctetString(p.GetEncoded()); + return encoding; } } } diff --git a/crypto/src/asn1/x9/X9FieldID.cs b/crypto/src/asn1/x9/X9FieldID.cs
index 58823a285..08d7d71b4 100644 --- a/crypto/src/asn1/x9/X9FieldID.cs +++ b/crypto/src/asn1/x9/X9FieldID.cs
@@ -90,11 +90,19 @@ namespace Org.BouncyCastle.Asn1.X9 this.parameters = new DerSequence(fieldIdParams); } - internal X9FieldID( - Asn1Sequence seq) + private X9FieldID(Asn1Sequence seq) { - this.id = (DerObjectIdentifier) seq[0]; - this.parameters = (Asn1Object) seq[1]; + this.id = DerObjectIdentifier.GetInstance(seq[0]); + this.parameters = seq[1].ToAsn1Object(); + } + + public static X9FieldID GetInstance(object obj) + { + if (obj is X9FieldID) + return (X9FieldID)obj; + if (obj == null) + return null; + return new X9FieldID(Asn1Sequence.GetInstance(obj)); } public DerObjectIdentifier Identifier diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
index 3c69fbdf5..2e08cd090 100644 --- a/crypto/src/bcpg/BcpgInputStream.cs +++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -105,19 +105,15 @@ namespace Org.BouncyCastle.Bcpg next = true; } - if (nextB >= 0) + if (nextB < 0) + return (PacketTag)nextB; + + int maskB = nextB & 0x3f; + if ((nextB & 0x40) == 0) // old { - if ((nextB & 0x40) != 0) // new - { - return (PacketTag)(nextB & 0x3f); - } - else // old - { - return (PacketTag)((nextB & 0x3f) >> 2); - } + maskB >>= 2; } - - return (PacketTag) nextB; + return (PacketTag)maskB; } public Packet ReadPacket() diff --git a/crypto/src/bcpg/DsaPublicBcpgKey.cs b/crypto/src/bcpg/DsaPublicBcpgKey.cs
index 61159567c..11294cc22 100644 --- a/crypto/src/bcpg/DsaPublicBcpgKey.cs +++ b/crypto/src/bcpg/DsaPublicBcpgKey.cs
@@ -4,46 +4,46 @@ using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Bcpg { - /// <remarks>Base class for a DSA public key.</remarks> - public class DsaPublicBcpgKey - : BcpgObject, IBcpgKey + /// <remarks>Base class for a DSA public key.</remarks> + public class DsaPublicBcpgKey + : BcpgObject, IBcpgKey { private readonly MPInteger p, q, g, y; - /// <param name="bcpgIn">The stream to read the packet from.</param> - public DsaPublicBcpgKey( - BcpgInputStream bcpgIn) - { - this.p = new MPInteger(bcpgIn); - this.q = new MPInteger(bcpgIn); - this.g = new MPInteger(bcpgIn); - this.y = new MPInteger(bcpgIn); - } + /// <param name="bcpgIn">The stream to read the packet from.</param> + public DsaPublicBcpgKey( + BcpgInputStream bcpgIn) + { + this.p = new MPInteger(bcpgIn); + this.q = new MPInteger(bcpgIn); + this.g = new MPInteger(bcpgIn); + this.y = new MPInteger(bcpgIn); + } - public DsaPublicBcpgKey( - BigInteger p, - BigInteger q, - BigInteger g, - BigInteger y) - { - this.p = new MPInteger(p); - this.q = new MPInteger(q); - this.g = new MPInteger(g); - this.y = new MPInteger(y); - } + public DsaPublicBcpgKey( + BigInteger p, + BigInteger q, + BigInteger g, + BigInteger y) + { + this.p = new MPInteger(p); + this.q = new MPInteger(q); + this.g = new MPInteger(g); + this.y = new MPInteger(y); + } - /// <summary>The format, as a string, always "PGP".</summary> - public string Format - { - get { return "PGP"; } - } + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } - /// <summary>Return the standard PGP encoding of the key.</summary> + /// <summary>Return the standard PGP encoding of the key.</summary> public override byte[] GetEncoded() { try { - return base.GetEncoded(); + return base.GetEncoded(); } catch (Exception) { @@ -51,30 +51,30 @@ namespace Org.BouncyCastle.Bcpg } } - public override void Encode( - BcpgOutputStream bcpgOut) - { - bcpgOut.WriteObjects(p, q, g, y); - } + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObjects(p, q, g, y); + } - public BigInteger G - { - get { return g.Value; } - } + public BigInteger G + { + get { return g.Value; } + } - public BigInteger P - { - get { return p.Value; } - } + public BigInteger P + { + get { return p.Value; } + } - public BigInteger Q - { - get { return q.Value; } - } + public BigInteger Q + { + get { return q.Value; } + } - public BigInteger Y - { - get { return y.Value; } - } + public BigInteger Y + { + get { return y.Value; } + } } } diff --git a/crypto/src/bcpg/ECDHPublicBCPGKey.cs b/crypto/src/bcpg/ECDHPublicBCPGKey.cs new file mode 100644
index 000000000..dc225e31e --- /dev/null +++ b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
@@ -0,0 +1,102 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an ECDH Public Key.</remarks> + public class ECDHPublicBcpgKey + : ECPublicBcpgKey + { + private byte reserved; + private HashAlgorithmTag hashFunctionId; + private SymmetricKeyAlgorithmTag symAlgorithmId; + + /// <param name="bcpgIn">The stream to read the packet from.</param> + public ECDHPublicBcpgKey( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + int length = bcpgIn.ReadByte(); + byte[] kdfParameters = new byte[length]; + if (kdfParameters.Length != 3) + throw new InvalidOperationException("kdf parameters size of 3 expected."); + + bcpgIn.ReadFully(kdfParameters); + + reserved = kdfParameters[0]; + hashFunctionId = (HashAlgorithmTag)kdfParameters[1]; + symAlgorithmId = (SymmetricKeyAlgorithmTag)kdfParameters[2]; + + VerifyHashAlgorithm(); + VerifySymmetricKeyAlgorithm(); + } + + public ECDHPublicBcpgKey( + DerObjectIdentifier oid, + ECPoint point, + HashAlgorithmTag hashAlgorithm, + SymmetricKeyAlgorithmTag symmetricKeyAlgorithm) + : base(oid, point) + { + reserved = 1; + hashFunctionId = hashAlgorithm; + symAlgorithmId = symmetricKeyAlgorithm; + + VerifyHashAlgorithm(); + VerifySymmetricKeyAlgorithm(); + } + + public virtual byte Reserved + { + get { return reserved; } + } + + public virtual HashAlgorithmTag HashAlgorithm + { + get { return hashFunctionId; } + } + + public virtual SymmetricKeyAlgorithmTag SymmetricKeyAlgorithm + { + get { return symAlgorithmId; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + base.Encode(bcpgOut); + bcpgOut.WriteByte(0x3); + bcpgOut.WriteByte(reserved); + bcpgOut.WriteByte((byte)hashFunctionId); + bcpgOut.WriteByte((byte)symAlgorithmId); + } + + private void VerifyHashAlgorithm() + { + switch ((HashAlgorithmTag)hashFunctionId) + { + case HashAlgorithmTag.Sha256: + case HashAlgorithmTag.Sha384: + case HashAlgorithmTag.Sha512: + break; + default: + throw new InvalidOperationException("Hash algorithm must be SHA-256 or stronger."); + } + } + + private void VerifySymmetricKeyAlgorithm() + { + switch ((SymmetricKeyAlgorithmTag)symAlgorithmId) + { + case SymmetricKeyAlgorithmTag.Aes128: + case SymmetricKeyAlgorithmTag.Aes192: + case SymmetricKeyAlgorithmTag.Aes256: + break; + default: + throw new InvalidOperationException("Symmetric key algorithm must be AES-128 or stronger."); + } + } + } +} diff --git a/crypto/src/bcpg/ECDsaPublicBCPGKey.cs b/crypto/src/bcpg/ECDsaPublicBCPGKey.cs new file mode 100644
index 000000000..5f0c8ac55 --- /dev/null +++ b/crypto/src/bcpg/ECDsaPublicBCPGKey.cs
@@ -0,0 +1,34 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an ECDSA Public Key.</remarks> + public class ECDsaPublicBcpgKey + : ECPublicBcpgKey + { + /// <param name="bcpgIn">The stream to read the packet from.</param> + protected internal ECDsaPublicBcpgKey( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + } + + public ECDsaPublicBcpgKey( + DerObjectIdentifier oid, + ECPoint point) + : base(oid, point) + { + } + + public ECDsaPublicBcpgKey( + DerObjectIdentifier oid, + BigInteger encodedPoint) + : base(oid, encodedPoint) + { + } + } +} diff --git a/crypto/src/bcpg/ECPublicBCPGKey.cs b/crypto/src/bcpg/ECPublicBCPGKey.cs new file mode 100644
index 000000000..f328f9dc3 --- /dev/null +++ b/crypto/src/bcpg/ECPublicBCPGKey.cs
@@ -0,0 +1,97 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an EC Public Key.</remarks> + public abstract class ECPublicBcpgKey + : BcpgObject, IBcpgKey + { + internal DerObjectIdentifier oid; + internal BigInteger point; + + /// <param name="bcpgIn">The stream to read the packet from.</param> + protected ECPublicBcpgKey( + BcpgInputStream bcpgIn) + { + this.oid = DerObjectIdentifier.GetInstance(Asn1Object.FromByteArray(ReadBytesOfEncodedLength(bcpgIn))); + this.point = new MPInteger(bcpgIn).Value; + } + + protected ECPublicBcpgKey( + DerObjectIdentifier oid, + ECPoint point) + { + this.point = new BigInteger(1, point.GetEncoded()); + this.oid = oid; + } + + protected ECPublicBcpgKey( + DerObjectIdentifier oid, + BigInteger encodedPoint) + { + this.point = encodedPoint; + this.oid = oid; + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (IOException) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + byte[] oid = this.oid.GetEncoded(); + bcpgOut.Write(oid, 1, oid.Length - 1); + + MPInteger point = new MPInteger(this.point); + bcpgOut.WriteObject(point); + } + + public virtual BigInteger EncodedPoint + { + get { return point; } + } + + public virtual DerObjectIdentifier CurveOid + { + get { return oid; } + } + + protected static byte[] ReadBytesOfEncodedLength( + BcpgInputStream bcpgIn) + { + int length = bcpgIn.ReadByte(); + if (length == 0 || length == 0xFF) + { + throw new IOException("future extensions not yet implemented."); + } + + byte[] buffer = new byte[length + 2]; + bcpgIn.ReadFully(buffer, 2, buffer.Length - 2); + buffer[0] = (byte)0x06; + buffer[1] = (byte)length; + + return buffer; + } + } +} diff --git a/crypto/src/bcpg/ECSecretBCPGKey.cs b/crypto/src/bcpg/ECSecretBCPGKey.cs new file mode 100644
index 000000000..22e0a3473 --- /dev/null +++ b/crypto/src/bcpg/ECSecretBCPGKey.cs
@@ -0,0 +1,56 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an EC Secret Key.</remarks> + public class ECSecretBcpgKey + : BcpgObject, IBcpgKey + { + internal MPInteger x; + + public ECSecretBcpgKey( + BcpgInputStream bcpgIn) + { + this.x = new MPInteger(bcpgIn); + } + + public ECSecretBcpgKey( + BigInteger x) + { + this.x = new MPInteger(x); + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObject(x); + } + + public virtual BigInteger X + { + get { return x.Value; } + } + } +} diff --git a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
index 85ae548eb..9e30b54f7 100644 --- a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs +++ b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
@@ -1,6 +1,8 @@ +using System; + namespace Org.BouncyCastle.Bcpg { - /// <remarks>Public Key Algorithm tag numbers.</remarks> + /// <remarks>Public Key Algorithm tag numbers.</remarks> public enum PublicKeyAlgorithmTag { RsaGeneral = 1, // RSA (Encrypt or Sign) @@ -8,21 +10,23 @@ namespace Org.BouncyCastle.Bcpg RsaSign = 3, // RSA Sign-Only ElGamalEncrypt = 16, // Elgamal (Encrypt-Only), see [ELGAMAL] Dsa = 17, // DSA (Digital Signature Standard) + [Obsolete("Use 'ECDH' instead")] EC = 18, // Reserved for Elliptic Curve + ECDH = 18, // Reserved for Elliptic Curve (actual algorithm name) ECDsa = 19, // Reserved for ECDSA ElGamalGeneral = 20, // Elgamal (Encrypt or Sign) DiffieHellman = 21, // Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME) - Experimental_1 = 100, - Experimental_2 = 101, - Experimental_3 = 102, - Experimental_4 = 103, - Experimental_5 = 104, - Experimental_6 = 105, - Experimental_7 = 106, - Experimental_8 = 107, - Experimental_9 = 108, - Experimental_10 = 109, - Experimental_11 = 110, - } + Experimental_1 = 100, + Experimental_2 = 101, + Experimental_3 = 102, + Experimental_4 = 103, + Experimental_5 = 104, + Experimental_6 = 105, + Experimental_7 = 106, + Experimental_8 = 107, + Experimental_9 = 108, + Experimental_10 = 109, + Experimental_11 = 110, + } } diff --git a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
index d10605f1d..74d04f7aa 100644 --- a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs +++ b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -2,6 +2,8 @@ using System; using System.IO; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Bcpg { @@ -12,7 +14,7 @@ namespace Org.BouncyCastle.Bcpg private int version; private long keyId; private PublicKeyAlgorithmTag algorithm; - private BigInteger[] data; + private byte[][] data; internal PublicKeyEncSessionPacket( BcpgInputStream bcpgIn) @@ -34,33 +36,41 @@ namespace Org.BouncyCastle.Bcpg { case PublicKeyAlgorithmTag.RsaEncrypt: case PublicKeyAlgorithmTag.RsaGeneral: - data = new BigInteger[]{ new MPInteger(bcpgIn).Value }; + data = new byte[][]{ new MPInteger(bcpgIn).GetEncoded() }; break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: - data = new BigInteger[] - { - new MPInteger(bcpgIn).Value, - new MPInteger(bcpgIn).Value - }; + MPInteger p = new MPInteger(bcpgIn); + MPInteger g = new MPInteger(bcpgIn); + data = new byte[][]{ + p.GetEncoded(), + g.GetEncoded(), + }; break; + case PublicKeyAlgorithmTag.ECDH: + data = new byte[][]{ Streams.ReadAll(bcpgIn) }; + break; default: throw new IOException("unknown PGP public key algorithm encountered"); } } - public PublicKeyEncSessionPacket( - long keyId, - PublicKeyAlgorithmTag algorithm, - BigInteger[] data) + public PublicKeyEncSessionPacket( + long keyId, + PublicKeyAlgorithmTag algorithm, + byte[][] data) { this.version = 3; this.keyId = keyId; this.algorithm = algorithm; - this.data = (BigInteger[]) data.Clone(); + this.data = new byte[data.Length][]; + for (int i = 0; i < data.Length; ++i) + { + this.data[i] = Arrays.Clone(data[i]); + } } - public int Version + public int Version { get { return version; } } @@ -75,12 +85,12 @@ namespace Org.BouncyCastle.Bcpg get { return algorithm; } } - public BigInteger[] GetEncSessionKey() + public byte[][] GetEncSessionKey() { - return (BigInteger[]) data.Clone(); + return data; } - public override void Encode( + public override void Encode( BcpgOutputStream bcpgOut) { MemoryStream bOut = new MemoryStream(); @@ -92,12 +102,14 @@ namespace Org.BouncyCastle.Bcpg pOut.WriteByte((byte)algorithm); - for (int i = 0; i != data.Length; i++) - { - MPInteger.Encode(pOut, data[i]); - } + for (int i = 0; i < data.Length; ++i) + { + pOut.Write(data[i]); + } + + pOut.Close(); - bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true); + bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true); } } } diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
index 32d43149b..bbed941dc 100644 --- a/crypto/src/bcpg/PublicKeyPacket.cs +++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -5,11 +5,11 @@ using Org.BouncyCastle.Utilities.Date; namespace Org.BouncyCastle.Bcpg { - /// <remarks>Basic packet for a PGP public key.</remarks> + /// <remarks>Basic packet for a PGP public key.</remarks> public class PublicKeyPacket : ContainedPacket //, PublicKeyAlgorithmTag { - private int version; + private int version; private long time; private int validDays; private PublicKeyAlgorithmTag algorithm; @@ -44,49 +44,55 @@ namespace Org.BouncyCastle.Bcpg case PublicKeyAlgorithmTag.ElGamalGeneral: key = new ElGamalPublicBcpgKey(bcpgIn); break; + case PublicKeyAlgorithmTag.ECDH: + key = new ECDHPublicBcpgKey(bcpgIn); + break; + case PublicKeyAlgorithmTag.ECDsa: + key = new ECDsaPublicBcpgKey(bcpgIn); + break; default: throw new IOException("unknown PGP public key algorithm encountered"); } } - /// <summary>Construct a version 4 public key packet.</summary> + /// <summary>Construct a version 4 public key packet.</summary> public PublicKeyPacket( PublicKeyAlgorithmTag algorithm, DateTime time, IBcpgKey key) { - this.version = 4; + this.version = 4; this.time = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L; this.algorithm = algorithm; this.key = key; } - public int Version + public virtual int Version { - get { return version; } + get { return version; } } - public PublicKeyAlgorithmTag Algorithm + public virtual PublicKeyAlgorithmTag Algorithm { - get { return algorithm; } + get { return algorithm; } } - public int ValidDays + public virtual int ValidDays { - get { return validDays; } + get { return validDays; } } - public DateTime GetTime() + public virtual DateTime GetTime() { return DateTimeUtilities.UnixMsToDateTime(time * 1000L); } - public IBcpgKey Key + public virtual IBcpgKey Key { - get { return key; } + get { return key; } } - public byte[] GetEncodedContents() + public virtual byte[] GetEncodedContents() { MemoryStream bOut = new MemoryStream(); BcpgOutputStream pOut = new BcpgOutputStream(bOut); @@ -94,22 +100,22 @@ namespace Org.BouncyCastle.Bcpg pOut.WriteByte((byte) version); pOut.WriteInt((int) time); - if (version <= 3) + if (version <= 3) { pOut.WriteShort((short) validDays); } - pOut.WriteByte((byte) algorithm); + pOut.WriteByte((byte) algorithm); - pOut.WriteObject((BcpgObject)key); + pOut.WriteObject((BcpgObject)key); - return bOut.ToArray(); + return bOut.ToArray(); } - public override void Encode( - BcpgOutputStream bcpgOut) - { - bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true); - } - } + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true); + } + } } diff --git a/crypto/src/bcpg/S2k.cs b/crypto/src/bcpg/S2k.cs
index de08c016c..33fd792fe 100644 --- a/crypto/src/bcpg/S2k.cs +++ b/crypto/src/bcpg/S2k.cs
@@ -16,6 +16,8 @@ namespace Org.BouncyCastle.Bcpg public const int Salted = 1; public const int SaltedAndIterated = 3; public const int GnuDummyS2K = 101; + public const int GnuProtectionModeNoPrivateKey = 1; + public const int GnuProtectionModeDivertToCard = 2; internal int type; internal HashAlgorithmTag algorithm; @@ -82,19 +84,19 @@ namespace Org.BouncyCastle.Bcpg this.itCount = itCount; } - public int Type + public virtual int Type { get { return type; } } /// <summary>The hash algorithm.</summary> - public HashAlgorithmTag HashAlgorithm + public virtual HashAlgorithmTag HashAlgorithm { get { return algorithm; } } /// <summary>The IV for the key generation algorithm.</summary> - public byte[] GetIV() + public virtual byte[] GetIV() { return Arrays.Clone(iv); } @@ -106,13 +108,13 @@ namespace Org.BouncyCastle.Bcpg } /// <summary>The iteration count</summary> - public long IterationCount + public virtual long IterationCount { get { return (16 + (itCount & 15)) << ((itCount >> 4) + ExpBias); } } /// <summary>The protection mode - only if GnuDummyS2K</summary> - public int ProtectionMode + public virtual int ProtectionMode { get { return protectionMode; } } diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs
index 605ce84c4..5b91c15a3 100644 --- a/crypto/src/bcpg/SignaturePacket.cs +++ b/crypto/src/bcpg/SignaturePacket.cs
@@ -146,6 +146,11 @@ namespace Org.BouncyCastle.Bcpg MPInteger y = new MPInteger(bcpgIn); signature = new MPInteger[]{ p, g, y }; break; + case PublicKeyAlgorithmTag.ECDsa: + MPInteger ecR = new MPInteger(bcpgIn); + MPInteger ecS = new MPInteger(bcpgIn); + signature = new MPInteger[]{ ecR, ecS }; + break; default: if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11) { diff --git a/crypto/src/bcpg/SignatureSubpacket.cs b/crypto/src/bcpg/SignatureSubpacket.cs
index ac26f8a3c..d99315599 100644 --- a/crypto/src/bcpg/SignatureSubpacket.cs +++ b/crypto/src/bcpg/SignatureSubpacket.cs
@@ -7,20 +7,22 @@ namespace Org.BouncyCastle.Bcpg { private readonly SignatureSubpacketTag type; private readonly bool critical; - - internal readonly byte[] data; + private readonly bool isLongLength; + internal byte[] data; protected internal SignatureSubpacket( SignatureSubpacketTag type, bool critical, + bool isLongLength, byte[] data) { this.type = type; this.critical = critical; + this.isLongLength = isLongLength; this.data = data; } - public SignatureSubpacketTag SubpacketType + public SignatureSubpacketTag SubpacketType { get { return type; } } @@ -30,7 +32,12 @@ namespace Org.BouncyCastle.Bcpg return critical; } - /// <summary>Return the generic data making up the packet.</summary> + public bool IsLongLength() + { + return isLongLength; + } + + /// <summary>Return the generic data making up the packet.</summary> public byte[] GetData() { return (byte[]) data.Clone(); @@ -41,18 +48,7 @@ namespace Org.BouncyCastle.Bcpg { int bodyLen = data.Length + 1; - if (bodyLen < 192) - { - os.WriteByte((byte)bodyLen); - } - else if (bodyLen <= 8383) - { - bodyLen -= 192; - - os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192)); - os.WriteByte((byte)bodyLen); - } - else + if (isLongLength) { os.WriteByte(0xff); os.WriteByte((byte)(bodyLen >> 24)); @@ -60,6 +56,28 @@ namespace Org.BouncyCastle.Bcpg os.WriteByte((byte)(bodyLen >> 8)); os.WriteByte((byte)bodyLen); } + else + { + if (bodyLen < 192) + { + os.WriteByte((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192)); + os.WriteByte((byte)bodyLen); + } + else + { + os.WriteByte(0xff); + os.WriteByte((byte)(bodyLen >> 24)); + os.WriteByte((byte)(bodyLen >> 16)); + os.WriteByte((byte)(bodyLen >> 8)); + os.WriteByte((byte)bodyLen); + } + } if (critical) { diff --git a/crypto/src/bcpg/SignatureSubpacketsReader.cs b/crypto/src/bcpg/SignatureSubpacketsReader.cs
index 8dd1af332..80bedb07c 100644 --- a/crypto/src/bcpg/SignatureSubpacketsReader.cs +++ b/crypto/src/bcpg/SignatureSubpacketsReader.cs
@@ -1,6 +1,8 @@ using System; using System.IO; + using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Bcpg @@ -25,7 +27,9 @@ namespace Org.BouncyCastle.Bcpg return null; int bodyLen = 0; - if (l < 192) + bool isLongLength = false; + + if (l < 192) { bodyLen = l; } @@ -35,54 +39,90 @@ namespace Org.BouncyCastle.Bcpg } else if (l == 255) { + isLongLength = true; bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16) | (input.ReadByte() << 8) | input.ReadByte(); } else { - // TODO Error? + throw new IOException("unexpected length header"); } - int tag = input.ReadByte(); + int tag = input.ReadByte(); if (tag < 0) throw new EndOfStreamException("unexpected EOF reading signature sub packet"); - byte[] data = new byte[bodyLen - 1]; - if (Streams.ReadFully(input, data) < data.Length) - throw new EndOfStreamException(); + byte[] data = new byte[bodyLen - 1]; + + // + // this may seem a bit strange but it turns out some applications miscode the length + // in fixed length fields, so we check the length we do get, only throwing an exception if + // we really cannot continue + // + int bytesRead = Streams.ReadFully(input, data); + + bool isCritical = ((tag & 0x80) != 0); + SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f); - bool isCritical = ((tag & 0x80) != 0); - SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f); - switch (type) + if (bytesRead != data.Length) + { + switch (type) + { + case SignatureSubpacketTag.CreationTime: + data = CheckData(data, 4, bytesRead, "Signature Creation Time"); + break; + case SignatureSubpacketTag.IssuerKeyId: + data = CheckData(data, 8, bytesRead, "Issuer"); + break; + case SignatureSubpacketTag.KeyExpireTime: + data = CheckData(data, 4, bytesRead, "Signature Key Expiration Time"); + break; + case SignatureSubpacketTag.ExpireTime: + data = CheckData(data, 4, bytesRead, "Signature Expiration Time"); + break; + default: + throw new EndOfStreamException("truncated subpacket data."); + } + } + + switch (type) { case SignatureSubpacketTag.CreationTime: - return new SignatureCreationTime(isCritical, data); + return new SignatureCreationTime(isCritical, isLongLength, data); case SignatureSubpacketTag.KeyExpireTime: - return new KeyExpirationTime(isCritical, data); + return new KeyExpirationTime(isCritical, isLongLength, data); case SignatureSubpacketTag.ExpireTime: - return new SignatureExpirationTime(isCritical, data); + return new SignatureExpirationTime(isCritical, isLongLength, data); case SignatureSubpacketTag.Revocable: - return new Revocable(isCritical, data); + return new Revocable(isCritical, isLongLength, data); case SignatureSubpacketTag.Exportable: - return new Exportable(isCritical, data); + return new Exportable(isCritical, isLongLength, data); case SignatureSubpacketTag.IssuerKeyId: - return new IssuerKeyId(isCritical, data); + return new IssuerKeyId(isCritical, isLongLength, data); case SignatureSubpacketTag.TrustSig: - return new TrustSignature(isCritical, data); + return new TrustSignature(isCritical, isLongLength, data); case SignatureSubpacketTag.PreferredCompressionAlgorithms: case SignatureSubpacketTag.PreferredHashAlgorithms: case SignatureSubpacketTag.PreferredSymmetricAlgorithms: - return new PreferredAlgorithms(type, isCritical, data); + return new PreferredAlgorithms(type, isCritical, isLongLength, data); case SignatureSubpacketTag.KeyFlags: - return new KeyFlags(isCritical, data); + return new KeyFlags(isCritical, isLongLength, data); case SignatureSubpacketTag.PrimaryUserId: - return new PrimaryUserId(isCritical, data); + return new PrimaryUserId(isCritical, isLongLength, data); case SignatureSubpacketTag.SignerUserId: - return new SignerUserId(isCritical, data); + return new SignerUserId(isCritical, isLongLength, data); case SignatureSubpacketTag.NotationData: - return new NotationData(isCritical, data); + return new NotationData(isCritical, isLongLength, data); } - return new SignatureSubpacket(type, isCritical, data); + return new SignatureSubpacket(type, isCritical, isLongLength, data); } + + private byte[] CheckData(byte[] data, int expected, int bytesRead, string name) + { + if (bytesRead != expected) + throw new EndOfStreamException("truncated " + name + " subpacket data."); + + return Arrays.CopyOfRange(data, 0, expected); + } } } diff --git a/crypto/src/bcpg/UserAttributeSubpacket.cs b/crypto/src/bcpg/UserAttributeSubpacket.cs
index bd49d2150..05f60ac17 100644 --- a/crypto/src/bcpg/UserAttributeSubpacket.cs +++ b/crypto/src/bcpg/UserAttributeSubpacket.cs
@@ -10,40 +10,44 @@ namespace Org.BouncyCastle.Bcpg */ public class UserAttributeSubpacket { - private readonly UserAttributeSubpacketTag type; - private readonly byte[] data; + internal readonly UserAttributeSubpacketTag type; + private readonly bool longLength; // we preserve this as not everyone encodes length properly. + protected readonly byte[] data; - internal UserAttributeSubpacket( - UserAttributeSubpacketTag type, - byte[] data) + protected internal UserAttributeSubpacket(UserAttributeSubpacketTag type, byte[] data) + : this(type, false, data) + { + } + + protected internal UserAttributeSubpacket(UserAttributeSubpacketTag type, bool forceLongLength, byte[] data) { this.type = type; + this.longLength = forceLongLength; this.data = data; } - public UserAttributeSubpacketTag SubpacketType + public virtual UserAttributeSubpacketTag SubpacketType { get { return type; } } - /** + /** * return the generic data making up the packet. */ - public byte[] GetData() + public virtual byte[] GetData() { return data; } - public void Encode( - Stream os) + public virtual void Encode(Stream os) { int bodyLen = data.Length + 1; - if (bodyLen < 192) + if (bodyLen < 192 && !longLength) { os.WriteByte((byte)bodyLen); } - else if (bodyLen <= 8383) + else if (bodyLen <= 8383 && !longLength) { bodyLen -= 192; @@ -69,18 +73,18 @@ namespace Org.BouncyCastle.Bcpg if (obj == this) return true; - UserAttributeSubpacket other = obj as UserAttributeSubpacket; + UserAttributeSubpacket other = obj as UserAttributeSubpacket; - if (other == null) - return false; + if (other == null) + return false; - return type == other.type - && Arrays.AreEqual(data, other.data); + return type == other.type + && Arrays.AreEqual(data, other.data); } - public override int GetHashCode() + public override int GetHashCode() { - return type.GetHashCode() ^ Arrays.GetHashCode(data); + return type.GetHashCode() ^ Arrays.GetHashCode(data); } } } diff --git a/crypto/src/bcpg/UserAttributeSubpacketsReader.cs b/crypto/src/bcpg/UserAttributeSubpacketsReader.cs
index 2e5ea0f3e..f0cc1b8e4 100644 --- a/crypto/src/bcpg/UserAttributeSubpacketsReader.cs +++ b/crypto/src/bcpg/UserAttributeSubpacketsReader.cs
@@ -5,59 +5,61 @@ using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Bcpg { - /** - * reader for user attribute sub-packets - */ - public class UserAttributeSubpacketsParser - { - private readonly Stream input; + /** + * reader for user attribute sub-packets + */ + public class UserAttributeSubpacketsParser + { + private readonly Stream input; - public UserAttributeSubpacketsParser( - Stream input) - { - this.input = input; - } + public UserAttributeSubpacketsParser( + Stream input) + { + this.input = input; + } - public UserAttributeSubpacket ReadPacket() - { - int l = input.ReadByte(); - if (l < 0) - return null; + public virtual UserAttributeSubpacket ReadPacket() + { + int l = input.ReadByte(); + if (l < 0) + return null; - int bodyLen = 0; - if (l < 192) - { - bodyLen = l; - } - else if (l <= 223) - { - bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192; - } - else if (l == 255) - { - bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16) - | (input.ReadByte() << 8) | input.ReadByte(); - } - else - { - // TODO Error? - } + int bodyLen = 0; + bool longLength = false; + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192; + } + else if (l == 255) + { + bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16) + | (input.ReadByte() << 8) | input.ReadByte(); + longLength = true; + } + else + { + throw new IOException("unrecognised length reading user attribute sub packet"); + } - int tag = input.ReadByte(); - if (tag < 0) - throw new EndOfStreamException("unexpected EOF reading user attribute sub packet"); + int tag = input.ReadByte(); + if (tag < 0) + throw new EndOfStreamException("unexpected EOF reading user attribute sub packet"); - byte[] data = new byte[bodyLen - 1]; - if (Streams.ReadFully(input, data) < data.Length) - throw new EndOfStreamException(); + byte[] data = new byte[bodyLen - 1]; + if (Streams.ReadFully(input, data) < data.Length) + throw new EndOfStreamException(); - UserAttributeSubpacketTag type = (UserAttributeSubpacketTag) tag; - switch (type) - { - case UserAttributeSubpacketTag.ImageAttribute: - return new ImageAttrib(data); - } - return new UserAttributeSubpacket(type, data); - } - } + UserAttributeSubpacketTag type = (UserAttributeSubpacketTag) tag; + switch (type) + { + case UserAttributeSubpacketTag.ImageAttribute: + return new ImageAttrib(longLength, data); + } + return new UserAttributeSubpacket(type, longLength, data); + } + } } diff --git a/crypto/src/bcpg/attr/ImageAttrib.cs b/crypto/src/bcpg/attr/ImageAttrib.cs
index 73490791c..2d0fef8b8 100644 --- a/crypto/src/bcpg/attr/ImageAttrib.cs +++ b/crypto/src/bcpg/attr/ImageAttrib.cs
@@ -3,25 +3,29 @@ using System.IO; namespace Org.BouncyCastle.Bcpg.Attr { - /// <remarks>Basic type for a image attribute packet.</remarks> + /// <remarks>Basic type for a image attribute packet.</remarks> public class ImageAttrib - : UserAttributeSubpacket + : UserAttributeSubpacket { - public enum Format : byte - { - Jpeg = 1 - } + public enum Format : byte + { + Jpeg = 1 + } - private static readonly byte[] Zeroes = new byte[12]; + private static readonly byte[] Zeroes = new byte[12]; - private int hdrLength; + private int hdrLength; private int _version; private int _encoding; private byte[] imageData; - public ImageAttrib( - byte[] data) - : base(UserAttributeSubpacketTag.ImageAttribute, data) + public ImageAttrib(byte[] data) + : this(false, data) + { + } + + public ImageAttrib(bool forceLongLength, byte[] data) + : base(UserAttributeSubpacketTag.ImageAttribute, forceLongLength, data) { hdrLength = ((data[1] & 0xff) << 8) | (data[0] & 0xff); _version = data[2] & 0xff; @@ -31,36 +35,36 @@ namespace Org.BouncyCastle.Bcpg.Attr Array.Copy(data, hdrLength, imageData, 0, imageData.Length); } - public ImageAttrib( - Format imageType, - byte[] imageData) - : this(ToByteArray(imageType, imageData)) - { - } + public ImageAttrib( + Format imageType, + byte[] imageData) + : this(ToByteArray(imageType, imageData)) + { + } - private static byte[] ToByteArray( - Format imageType, - byte[] imageData) - { - MemoryStream bOut = new MemoryStream(); - bOut.WriteByte(0x10); bOut.WriteByte(0x00); bOut.WriteByte(0x01); - bOut.WriteByte((byte) imageType); - bOut.Write(Zeroes, 0, Zeroes.Length); - bOut.Write(imageData, 0, imageData.Length); - return bOut.ToArray(); - } + private static byte[] ToByteArray( + Format imageType, + byte[] imageData) + { + MemoryStream bOut = new MemoryStream(); + bOut.WriteByte(0x10); bOut.WriteByte(0x00); bOut.WriteByte(0x01); + bOut.WriteByte((byte) imageType); + bOut.Write(Zeroes, 0, Zeroes.Length); + bOut.Write(imageData, 0, imageData.Length); + return bOut.ToArray(); + } - public int Version + public virtual int Version { - get { return _version; } + get { return _version; } } - public int Encoding + public virtual int Encoding { - get { return _encoding; } + get { return _encoding; } } - public byte[] GetImageData() + public virtual byte[] GetImageData() { return imageData; } diff --git a/crypto/src/bcpg/sig/EmbeddedSignature.cs b/crypto/src/bcpg/sig/EmbeddedSignature.cs
index e47604ac8..fffdaef73 100644 --- a/crypto/src/bcpg/sig/EmbeddedSignature.cs +++ b/crypto/src/bcpg/sig/EmbeddedSignature.cs
@@ -10,8 +10,9 @@ namespace Org.BouncyCastle.Bcpg.Sig { public EmbeddedSignature( bool critical, + bool isLongLength, byte[] data) - : base(SignatureSubpacketTag.EmbeddedSignature, critical, data) + : base(SignatureSubpacketTag.EmbeddedSignature, critical, isLongLength, data) { } } diff --git a/crypto/src/bcpg/sig/Exportable.cs b/crypto/src/bcpg/sig/Exportable.cs
index 4455c3814..4d030346f 100644 --- a/crypto/src/bcpg/sig/Exportable.cs +++ b/crypto/src/bcpg/sig/Exportable.cs
@@ -1,7 +1,5 @@ using System; - - namespace Org.BouncyCastle.Bcpg.Sig { /** @@ -27,15 +25,16 @@ namespace Org.BouncyCastle.Bcpg.Sig public Exportable( bool critical, - byte[] data) - : base(SignatureSubpacketTag.Exportable, critical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.Exportable, critical, isLongLength, data) { } public Exportable( bool critical, bool isExportable) - : base(SignatureSubpacketTag.Exportable, critical, BooleanToByteArray(isExportable)) + : base(SignatureSubpacketTag.Exportable, critical, false, BooleanToByteArray(isExportable)) { } diff --git a/crypto/src/bcpg/sig/Features.cs b/crypto/src/bcpg/sig/Features.cs new file mode 100644
index 000000000..29584239a --- /dev/null +++ b/crypto/src/bcpg/sig/Features.cs
@@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving signature expiration time. + */ + public class Features + : SignatureSubpacket + { + /** Identifier for the modification detection feature */ + public static readonly byte FEATURE_MODIFICATION_DETECTION = 1; + + private static byte[] FeatureToByteArray(byte feature) + { + return new byte[]{ feature }; + } + + public Features( + bool critical, + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.Features, critical, isLongLength, data) + { + } + + public Features(bool critical, byte feature) + : base(SignatureSubpacketTag.Features, critical, false, FeatureToByteArray(feature)) + { + } + + /** + * Returns if modification detection is supported. + */ + public bool SupportsModificationDetection + { + get { return SupportsFeature(FEATURE_MODIFICATION_DETECTION); } + } + + /** + * Returns if a particular feature is supported. + */ + public bool SupportsFeature(byte feature) + { + return Array.IndexOf(data, feature) >= 0; + } + + /** + * Sets support for a particular feature. + */ + private void SetSupportsFeature(byte feature, bool support) + { + if (feature == 0) + throw new ArgumentException("cannot be 0", "feature"); + + int i = Array.IndexOf(data, feature); + if ((i >= 0) == support) + return; + + if (support) + { + data = Arrays.Append(data, feature); + } + else + { + byte[] temp = new byte[data.Length - 1]; + Array.Copy(data, 0, temp, 0, i); + Array.Copy(data, i + 1, temp, i, temp.Length - i); + data = temp; + } + } + } +} diff --git a/crypto/src/bcpg/sig/IssuerKeyId.cs b/crypto/src/bcpg/sig/IssuerKeyId.cs
index 91490d33b..627ea3ecf 100644 --- a/crypto/src/bcpg/sig/IssuerKeyId.cs +++ b/crypto/src/bcpg/sig/IssuerKeyId.cs
@@ -29,15 +29,16 @@ namespace Org.BouncyCastle.Bcpg.Sig public IssuerKeyId( bool critical, - byte[] data) - : base(SignatureSubpacketTag.IssuerKeyId, critical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.IssuerKeyId, critical, isLongLength, data) { } public IssuerKeyId( bool critical, - long keyId) - : base(SignatureSubpacketTag.IssuerKeyId, critical, KeyIdToBytes(keyId)) + long keyId) + : base(SignatureSubpacketTag.IssuerKeyId, critical, false, KeyIdToBytes(keyId)) { } diff --git a/crypto/src/bcpg/sig/KeyExpirationTime.cs b/crypto/src/bcpg/sig/KeyExpirationTime.cs
index 23b4cac29..dfd3e76fd 100644 --- a/crypto/src/bcpg/sig/KeyExpirationTime.cs +++ b/crypto/src/bcpg/sig/KeyExpirationTime.cs
@@ -1,7 +1,5 @@ using System; - - namespace Org.BouncyCastle.Bcpg.Sig { /** @@ -25,15 +23,16 @@ namespace Org.BouncyCastle.Bcpg.Sig public KeyExpirationTime( bool critical, - byte[] data) - : base(SignatureSubpacketTag.KeyExpireTime, critical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.KeyExpireTime, critical, isLongLength, data) { } public KeyExpirationTime( bool critical, - long seconds) - : base(SignatureSubpacketTag.KeyExpireTime, critical, TimeToBytes(seconds)) + long seconds) + : base(SignatureSubpacketTag.KeyExpireTime, critical, false, TimeToBytes(seconds)) { } diff --git a/crypto/src/bcpg/sig/KeyFlags.cs b/crypto/src/bcpg/sig/KeyFlags.cs
index 0592301b3..5b5d85a72 100644 --- a/crypto/src/bcpg/sig/KeyFlags.cs +++ b/crypto/src/bcpg/sig/KeyFlags.cs
@@ -40,15 +40,16 @@ namespace Org.BouncyCastle.Bcpg.Sig public KeyFlags( bool critical, - byte[] data) - : base(SignatureSubpacketTag.KeyFlags, critical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.KeyFlags, critical, isLongLength, data) { } public KeyFlags( - bool critical, - int flags) - : base(SignatureSubpacketTag.KeyFlags, critical, IntToByteArray(flags)) + bool critical, + int flags) + : base(SignatureSubpacketTag.KeyFlags, critical, false, IntToByteArray(flags)) { } diff --git a/crypto/src/bcpg/sig/NotationData.cs b/crypto/src/bcpg/sig/NotationData.cs
index ccc9aa745..9ac6f89cf 100644 --- a/crypto/src/bcpg/sig/NotationData.cs +++ b/crypto/src/bcpg/sig/NotationData.cs
@@ -17,8 +17,9 @@ namespace Org.BouncyCastle.Bcpg.Sig public NotationData( bool critical, - byte[] data) - : base(SignatureSubpacketTag.NotationData, critical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.NotationData, critical, isLongLength, data) { } @@ -27,12 +28,12 @@ namespace Org.BouncyCastle.Bcpg.Sig bool humanReadable, string notationName, string notationValue) - : base(SignatureSubpacketTag.NotationData, critical, - createData(humanReadable, notationName, notationValue)) + : base(SignatureSubpacketTag.NotationData, critical, false, + CreateData(humanReadable, notationName, notationValue)) { } - private static byte[] createData( + private static byte[] CreateData( bool humanReadable, string notationName, string notationValue) diff --git a/crypto/src/bcpg/sig/PreferredAlgorithms.cs b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
index 0f282a38c..9514bed2b 100644 --- a/crypto/src/bcpg/sig/PreferredAlgorithms.cs +++ b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
@@ -1,7 +1,5 @@ using System; - - namespace Org.BouncyCastle.Bcpg.Sig { /** @@ -24,24 +22,25 @@ namespace Org.BouncyCastle.Bcpg.Sig } public PreferredAlgorithms( - SignatureSubpacketTag type, - bool critical, - byte[] data) - : base(type, critical, data) + SignatureSubpacketTag type, + bool critical, + bool isLongLength, + byte[] data) + : base(type, critical, isLongLength, data) { } public PreferredAlgorithms( - SignatureSubpacketTag type, - bool critical, - int[] preferences) - : base(type, critical, IntToByteArray(preferences)) + SignatureSubpacketTag type, + bool critical, + int[] preferences) + : base(type, critical, false, IntToByteArray(preferences)) { } public int[] GetPreferences() { - int[] v = new int[data.Length]; + int[] v = new int[data.Length]; for (int i = 0; i != v.Length; i++) { diff --git a/crypto/src/bcpg/sig/PrimaryUserId.cs b/crypto/src/bcpg/sig/PrimaryUserId.cs
index fc0353afd..1f16f40eb 100644 --- a/crypto/src/bcpg/sig/PrimaryUserId.cs +++ b/crypto/src/bcpg/sig/PrimaryUserId.cs
@@ -1,7 +1,5 @@ using System; - - namespace Org.BouncyCastle.Bcpg.Sig { /** @@ -28,15 +26,16 @@ namespace Org.BouncyCastle.Bcpg.Sig public PrimaryUserId( bool critical, - byte[] data) - : base(SignatureSubpacketTag.PrimaryUserId, critical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.PrimaryUserId, critical, isLongLength, data) { } public PrimaryUserId( bool critical, bool isPrimaryUserId) - : base(SignatureSubpacketTag.PrimaryUserId, critical, BooleanToByteArray(isPrimaryUserId)) + : base(SignatureSubpacketTag.PrimaryUserId, critical, false, BooleanToByteArray(isPrimaryUserId)) { } diff --git a/crypto/src/bcpg/sig/Revocable.cs b/crypto/src/bcpg/sig/Revocable.cs
index b5e94feec..7aa91391f 100644 --- a/crypto/src/bcpg/sig/Revocable.cs +++ b/crypto/src/bcpg/sig/Revocable.cs
@@ -1,7 +1,5 @@ using System; - - namespace Org.BouncyCastle.Bcpg.Sig { /** @@ -28,16 +26,17 @@ namespace Org.BouncyCastle.Bcpg.Sig public Revocable( bool critical, - byte[] data) - : base(SignatureSubpacketTag.Revocable, critical, data) - { + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.Revocable, critical, isLongLength, data) + { } public Revocable( bool critical, bool isRevocable) - : base(SignatureSubpacketTag.Revocable, critical, BooleanToByteArray(isRevocable)) - { + : base(SignatureSubpacketTag.Revocable, critical, false, BooleanToByteArray(isRevocable)) + { } public bool IsRevocable() diff --git a/crypto/src/bcpg/sig/RevocationKey.cs b/crypto/src/bcpg/sig/RevocationKey.cs
index 66982cb5a..11467d2af 100644 --- a/crypto/src/bcpg/sig/RevocationKey.cs +++ b/crypto/src/bcpg/sig/RevocationKey.cs
@@ -14,17 +14,18 @@ namespace Org.BouncyCastle.Bcpg // 20 octets of fingerprint public RevocationKey( bool isCritical, - byte[] data) - : base(SignatureSubpacketTag.RevocationKey, isCritical, data) + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.RevocationKey, isCritical, isLongLength, data) { } - public RevocationKey( + public RevocationKey( bool isCritical, RevocationKeyTag signatureClass, PublicKeyAlgorithmTag keyAlgorithm, byte[] fingerprint) - : base(SignatureSubpacketTag.RevocationKey, isCritical, + : base(SignatureSubpacketTag.RevocationKey, isCritical, false, CreateData(signatureClass, keyAlgorithm, fingerprint)) { } diff --git a/crypto/src/bcpg/sig/RevocationReason.cs b/crypto/src/bcpg/sig/RevocationReason.cs
index 98e9b0a3d..42afd5f5b 100644 --- a/crypto/src/bcpg/sig/RevocationReason.cs +++ b/crypto/src/bcpg/sig/RevocationReason.cs
@@ -11,16 +11,16 @@ namespace Org.BouncyCastle.Bcpg public class RevocationReason : SignatureSubpacket { - public RevocationReason(bool isCritical, byte[] data) - : base(SignatureSubpacketTag.RevocationReason, isCritical, data) + public RevocationReason(bool isCritical, bool isLongLength, byte[] data) + : base(SignatureSubpacketTag.RevocationReason, isCritical, isLongLength, data) { } - public RevocationReason( - bool isCritical, - RevocationReasonTag reason, - string description) - : base(SignatureSubpacketTag.RevocationReason, isCritical, CreateData(reason, description)) + public RevocationReason( + bool isCritical, + RevocationReasonTag reason, + string description) + : base(SignatureSubpacketTag.RevocationReason, isCritical, false, CreateData(reason, description)) { } diff --git a/crypto/src/bcpg/sig/SignatureCreationTime.cs b/crypto/src/bcpg/sig/SignatureCreationTime.cs
index e6f241f11..d172e5d52 100644 --- a/crypto/src/bcpg/sig/SignatureCreationTime.cs +++ b/crypto/src/bcpg/sig/SignatureCreationTime.cs
@@ -21,18 +21,22 @@ namespace Org.BouncyCastle.Bcpg.Sig data[3] = (byte)t; return data; } + public SignatureCreationTime( - bool critical, - byte[] data) - : base(SignatureSubpacketTag.CreationTime, critical, data) + bool critical, + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.CreationTime, critical, isLongLength, data) { } + public SignatureCreationTime( - bool critical, - DateTime date) - : base(SignatureSubpacketTag.CreationTime, critical, TimeToBytes(date)) + bool critical, + DateTime date) + : base(SignatureSubpacketTag.CreationTime, critical, false, TimeToBytes(date)) { } + public DateTime GetTime() { long time = (long)( diff --git a/crypto/src/bcpg/sig/SignatureExpirationTime.cs b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
index 7fddf5743..24f0a9f8a 100644 --- a/crypto/src/bcpg/sig/SignatureExpirationTime.cs +++ b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
@@ -1,7 +1,5 @@ using System; - - namespace Org.BouncyCastle.Bcpg.Sig { /** @@ -11,29 +9,28 @@ namespace Org.BouncyCastle.Bcpg.Sig : SignatureSubpacket { protected static byte[] TimeToBytes( - long t) + long t) { - byte[] data = new byte[4]; - + byte[] data = new byte[4]; data[0] = (byte)(t >> 24); data[1] = (byte)(t >> 16); data[2] = (byte)(t >> 8); data[3] = (byte)t; - return data; } public SignatureExpirationTime( bool critical, - byte[] data) - : base(SignatureSubpacketTag.ExpireTime, critical, data) - { + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.ExpireTime, critical, isLongLength, data) + { } public SignatureExpirationTime( bool critical, - long seconds) - : base(SignatureSubpacketTag.ExpireTime, critical, TimeToBytes(seconds)) + long seconds) + : base(SignatureSubpacketTag.ExpireTime, critical, false, TimeToBytes(seconds)) { } diff --git a/crypto/src/bcpg/sig/SignerUserId.cs b/crypto/src/bcpg/sig/SignerUserId.cs
index 98cc808e7..8ab62ed2e 100644 --- a/crypto/src/bcpg/sig/SignerUserId.cs +++ b/crypto/src/bcpg/sig/SignerUserId.cs
@@ -24,20 +24,21 @@ namespace Org.BouncyCastle.Bcpg.Sig } public SignerUserId( - bool critical, - byte[] data) - : base(SignatureSubpacketTag.SignerUserId, critical, data) + bool critical, + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.SignerUserId, critical, isLongLength, data) { } - public SignerUserId( - bool critical, - string userId) - : base(SignatureSubpacketTag.SignerUserId, critical, UserIdToBytes(userId)) + public SignerUserId( + bool critical, + string userId) + : base(SignatureSubpacketTag.SignerUserId, critical, false, UserIdToBytes(userId)) { } - public string GetId() + public string GetId() { char[] chars = new char[data.Length]; diff --git a/crypto/src/bcpg/sig/TrustSignature.cs b/crypto/src/bcpg/sig/TrustSignature.cs
index bbadd3067..91458826d 100644 --- a/crypto/src/bcpg/sig/TrustSignature.cs +++ b/crypto/src/bcpg/sig/TrustSignature.cs
@@ -16,17 +16,18 @@ namespace Org.BouncyCastle.Bcpg.Sig } public TrustSignature( - bool critical, - byte[] data) - : base(SignatureSubpacketTag.TrustSig, critical, data) + bool critical, + bool isLongLength, + byte[] data) + : base(SignatureSubpacketTag.TrustSig, critical, isLongLength, data) { } public TrustSignature( - bool critical, - int depth, - int trustAmount) - : base(SignatureSubpacketTag.TrustSig, critical, IntToByteArray(depth, trustAmount)) + bool critical, + int depth, + int trustAmount) + : base(SignatureSubpacketTag.TrustSig, critical, false, IntToByteArray(depth, trustAmount)) { } diff --git a/crypto/src/cms/CMSSignedDataGenerator.cs b/crypto/src/cms/CMSSignedDataGenerator.cs
index f31105c41..201cfc5c4 100644 --- a/crypto/src/cms/CMSSignedDataGenerator.cs +++ b/crypto/src/cms/CMSSignedDataGenerator.cs
@@ -6,11 +6,11 @@ using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cms; 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.Utilities; using Org.BouncyCastle.X509; +using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.Cms { @@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Cms { private readonly CmsSignedGenerator outer; - private readonly AsymmetricKeyParameter key; + private readonly ISignatureCalculator sigCalc; private readonly SignerIdentifier signerIdentifier; private readonly string digestOID; private readonly string encOID; @@ -61,8 +61,12 @@ namespace Org.BouncyCastle.Cms CmsAttributeTableGenerator unsAttr, Asn1.Cms.AttributeTable baseSignedTable) { + string digestName = Helper.GetDigestAlgName(digestOID); + + string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID); + this.outer = outer; - this.key = key; + this.sigCalc = new Asn1SignatureCalculator(signatureName, key); this.signerIdentifier = signerIdentifier; this.digestOID = digestOID; this.encOID = encOID; @@ -71,7 +75,25 @@ namespace Org.BouncyCastle.Cms this.baseSignedTable = baseSignedTable; } - internal AlgorithmIdentifier DigestAlgorithmID + internal SignerInf( + CmsSignedGenerator outer, + ISignatureCalculator sigCalc, + SignerIdentifier signerIdentifier, + CmsAttributeTableGenerator sAttr, + CmsAttributeTableGenerator unsAttr, + Asn1.Cms.AttributeTable baseSignedTable) + { + this.outer = outer; + this.sigCalc = sigCalc; + this.signerIdentifier = signerIdentifier; + this.digestOID = new DefaultDigestAlgorithmIdentifierFinder().find((AlgorithmIdentifier)sigCalc.AlgorithmDetails).Algorithm.Id; + this.encOID = ((AlgorithmIdentifier)sigCalc.AlgorithmDetails).Algorithm.Id; + this.sAttr = sAttr; + this.unsAttr = unsAttr; + this.baseSignedTable = baseSignedTable; + } + + internal AlgorithmIdentifier DigestAlgorithmID { get { return new AlgorithmIdentifier(new DerObjectIdentifier(digestOID), DerNull.Instance); } } @@ -93,25 +115,31 @@ namespace Org.BouncyCastle.Cms { AlgorithmIdentifier digAlgId = DigestAlgorithmID; string digestName = Helper.GetDigestAlgName(digestOID); - IDigest dig = Helper.GetDigestInstance(digestName); string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID); - ISigner sig = Helper.GetSignatureInstance(signatureName); - - // TODO Optimise the case where more than one signer with same digest - if (content != null) + + byte[] hash; + if (outer._digests.Contains(digestOID)) { - content.Write(new DigOutputStream(dig)); - } + hash = (byte[])outer._digests[digestOID]; + } + else + { + IDigest dig = Helper.GetDigestInstance(digestName); + if (content != null) + { + content.Write(new DigOutputStream(dig)); + } + hash = DigestUtilities.DoFinal(dig); + outer._digests.Add(digestOID, hash.Clone()); + } - byte[] hash = DigestUtilities.DoFinal(dig); - outer._digests.Add(digestOID, hash.Clone()); + IStreamCalculator calculator = sigCalc.CreateCalculator(); - sig.Init(true, new ParametersWithRandom(key, random)); #if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT - Stream sigStr = new SigOutputStream(sig); + Stream sigStr = new SigOutputStream(calculator.Stream); #else - Stream sigStr = new BufferedStream(new SigOutputStream(sig)); + Stream sigStr = new BufferedStream(calculator.Stream); #endif Asn1Set signedAttr = null; @@ -146,7 +174,7 @@ namespace Org.BouncyCastle.Cms } sigStr.Close(); - byte[] sigBytes = sig.GenerateSignature(); + byte[] sigBytes = ((IBlockResult)calculator.GetResult()).DoFinal(); Asn1Set unsignedAttr = null; if (unsAttr != null) @@ -164,7 +192,7 @@ namespace Org.BouncyCastle.Cms // TODO[RSAPSS] Need the ability to specify non-default parameters Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName); - AlgorithmIdentifier encAlgId = CmsSignedGenerator.GetEncAlgorithmIdentifier( + AlgorithmIdentifier encAlgId = Helper.GetEncAlgorithmIdentifier( new DerObjectIdentifier(encOID), sigX509Parameters); return new SignerInfo(signerIdentifier, digAlgId, @@ -197,7 +225,7 @@ namespace Org.BouncyCastle.Cms X509Certificate cert, string digestOID) { - AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID); + AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOID), digestOID); } /** @@ -228,7 +256,7 @@ namespace Org.BouncyCastle.Cms byte[] subjectKeyID, string digestOID) { - AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID); + AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOID), digestOID); } /** @@ -261,7 +289,7 @@ namespace Org.BouncyCastle.Cms Asn1.Cms.AttributeTable signedAttr, Asn1.Cms.AttributeTable unsignedAttr) { - AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID, + AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOID), digestOID, signedAttr, unsignedAttr); } @@ -305,7 +333,7 @@ namespace Org.BouncyCastle.Cms Asn1.Cms.AttributeTable signedAttr, Asn1.Cms.AttributeTable unsignedAttr) { - AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID, + AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOID), digestOID, signedAttr, unsignedAttr); } @@ -343,7 +371,7 @@ namespace Org.BouncyCastle.Cms CmsAttributeTableGenerator signedAttrGen, CmsAttributeTableGenerator unsignedAttrGen) { - AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID, + AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOID), digestOID, signedAttrGen, unsignedAttrGen); } @@ -372,7 +400,7 @@ namespace Org.BouncyCastle.Cms CmsAttributeTableGenerator signedAttrGen, CmsAttributeTableGenerator unsignedAttrGen) { - AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID, + AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOID), digestOID, signedAttrGen, unsignedAttrGen); } @@ -391,7 +419,13 @@ namespace Org.BouncyCastle.Cms signedAttrGen, unsignedAttrGen, null); } - private void doAddSigner( + public void AddSignerInfoGenerator(SignerInfoGenerator signerInfoGenerator) + { + signerInfs.Add(new SignerInf(this, signerInfoGenerator.contentSigner, signerInfoGenerator.sigId, + signerInfoGenerator.signedGen, signerInfoGenerator.unsignedGen, null)); + } + + private void doAddSigner( AsymmetricKeyParameter privateKey, SignerIdentifier signerIdentifier, string encryptionOID, diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
index 743e9c6c1..223fdb39d 100644 --- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs +++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -52,10 +52,10 @@ namespace Org.BouncyCastle.Cms private class DigestAndSignerInfoGeneratorHolder { - internal readonly SignerInfoGenerator signerInf; + internal readonly ISignerInfoGenerator signerInf; internal readonly string digestOID; - internal DigestAndSignerInfoGeneratorHolder(SignerInfoGenerator signerInf, String digestOID) + internal DigestAndSignerInfoGeneratorHolder(ISignerInfoGenerator signerInf, String digestOID) { this.signerInf = signerInf; this.digestOID = digestOID; @@ -67,7 +67,7 @@ namespace Org.BouncyCastle.Cms } } - private class SignerInfoGeneratorImpl : SignerInfoGenerator + private class SignerInfoGeneratorImpl : ISignerInfoGenerator { private readonly CmsSignedDataStreamGenerator outer; @@ -215,7 +215,7 @@ namespace Org.BouncyCastle.Cms // TODO[RSAPSS] Need the ability to specify non-default parameters Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName); - AlgorithmIdentifier digestEncryptionAlgorithm = CmsSignedGenerator.GetEncAlgorithmIdentifier( + AlgorithmIdentifier digestEncryptionAlgorithm = Helper.GetEncAlgorithmIdentifier( new DerObjectIdentifier(_encOID), sigX509Parameters); return new SignerInfo(_signerIdentifier, digestAlgorithm, @@ -347,7 +347,7 @@ namespace Org.BouncyCastle.Cms CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator) { - AddSigner(privateKey, cert, GetEncOid(privateKey, digestOid), digestOid, + AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOid), digestOid, signedAttrGenerator, unsignedAttrGenerator); } @@ -420,7 +420,7 @@ namespace Org.BouncyCastle.Cms CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator) { - AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOid), + AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOid), digestOid, signedAttrGenerator, unsignedAttrGenerator); } diff --git a/crypto/src/cms/CMSSignedGenerator.cs b/crypto/src/cms/CMSSignedGenerator.cs
index f272c830e..0fb1f314d 100644 --- a/crypto/src/cms/CMSSignedGenerator.cs +++ b/crypto/src/cms/CMSSignedGenerator.cs
@@ -16,12 +16,106 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; -using Org.BouncyCastle.Utilities.IO; using Org.BouncyCastle.X509; using Org.BouncyCastle.X509.Store; namespace Org.BouncyCastle.Cms { + public class DefaultDigestAlgorithmIdentifierFinder + { + private static readonly IDictionary digestOids = Platform.CreateHashtable(); + private static readonly IDictionary digestNameToOids = Platform.CreateHashtable(); + + static DefaultDigestAlgorithmIdentifierFinder() + { + // + // digests + // + digestOids.Add(OiwObjectIdentifiers.MD4WithRsaEncryption, PkcsObjectIdentifiers.MD4); + digestOids.Add(OiwObjectIdentifiers.MD4WithRsa, PkcsObjectIdentifiers.MD4); + digestOids.Add(OiwObjectIdentifiers.Sha1WithRsa, OiwObjectIdentifiers.IdSha1); + + digestOids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, NistObjectIdentifiers.IdSha224); + digestOids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, NistObjectIdentifiers.IdSha256); + digestOids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, NistObjectIdentifiers.IdSha384); + digestOids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, NistObjectIdentifiers.IdSha512); + digestOids.Add(PkcsObjectIdentifiers.MD2WithRsaEncryption, PkcsObjectIdentifiers.MD2); + digestOids.Add(PkcsObjectIdentifiers.MD4WithRsaEncryption, PkcsObjectIdentifiers.MD4); + digestOids.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption, PkcsObjectIdentifiers.MD5); + digestOids.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption, OiwObjectIdentifiers.IdSha1); + + digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha1, OiwObjectIdentifiers.IdSha1); + digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha224, NistObjectIdentifiers.IdSha224); + digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha256, NistObjectIdentifiers.IdSha256); + digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha384, NistObjectIdentifiers.IdSha384); + digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha512, NistObjectIdentifiers.IdSha512); + digestOids.Add(X9ObjectIdentifiers.IdDsaWithSha1, OiwObjectIdentifiers.IdSha1); + + digestOids.Add(NistObjectIdentifiers.DsaWithSha224, NistObjectIdentifiers.IdSha224); + digestOids.Add(NistObjectIdentifiers.DsaWithSha256, NistObjectIdentifiers.IdSha256); + digestOids.Add(NistObjectIdentifiers.DsaWithSha384, NistObjectIdentifiers.IdSha384); + digestOids.Add(NistObjectIdentifiers.DsaWithSha512, NistObjectIdentifiers.IdSha512); + + digestOids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128, TeleTrusTObjectIdentifiers.RipeMD128); + digestOids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160, TeleTrusTObjectIdentifiers.RipeMD160); + digestOids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256, TeleTrusTObjectIdentifiers.RipeMD256); + + digestOids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, CryptoProObjectIdentifiers.GostR3411); + digestOids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, CryptoProObjectIdentifiers.GostR3411); + + digestNameToOids.Add("SHA-1", OiwObjectIdentifiers.IdSha1); + digestNameToOids.Add("SHA-224", NistObjectIdentifiers.IdSha224); + digestNameToOids.Add("SHA-256", NistObjectIdentifiers.IdSha256); + digestNameToOids.Add("SHA-384", NistObjectIdentifiers.IdSha384); + digestNameToOids.Add("SHA-512", NistObjectIdentifiers.IdSha512); + + digestNameToOids.Add("SHA1", OiwObjectIdentifiers.IdSha1); + digestNameToOids.Add("SHA224", NistObjectIdentifiers.IdSha224); + digestNameToOids.Add("SHA256", NistObjectIdentifiers.IdSha256); + digestNameToOids.Add("SHA384", NistObjectIdentifiers.IdSha384); + digestNameToOids.Add("SHA512", NistObjectIdentifiers.IdSha512); + + digestNameToOids.Add("SHA3-224", NistObjectIdentifiers.IdSha3_224); + digestNameToOids.Add("SHA3-256", NistObjectIdentifiers.IdSha3_256); + digestNameToOids.Add("SHA3-384", NistObjectIdentifiers.IdSha3_384); + digestNameToOids.Add("SHA3-512", NistObjectIdentifiers.IdSha3_512); + + digestNameToOids.Add("SHAKE-128", NistObjectIdentifiers.IdShake128); + digestNameToOids.Add("SHAKE-256", NistObjectIdentifiers.IdShake256); + + digestNameToOids.Add("GOST3411", CryptoProObjectIdentifiers.GostR3411); + + digestNameToOids.Add("MD2", PkcsObjectIdentifiers.MD2); + digestNameToOids.Add("MD4", PkcsObjectIdentifiers.MD4); + digestNameToOids.Add("MD5", PkcsObjectIdentifiers.MD5); + + digestNameToOids.Add("RIPEMD128", TeleTrusTObjectIdentifiers.RipeMD128); + digestNameToOids.Add("RIPEMD160", TeleTrusTObjectIdentifiers.RipeMD160); + digestNameToOids.Add("RIPEMD256", TeleTrusTObjectIdentifiers.RipeMD256); + } + + public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId) + { + AlgorithmIdentifier digAlgId; + + if (sigAlgId.Algorithm.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + digAlgId = RsassaPssParameters.GetInstance(sigAlgId.Parameters).HashAlgorithm; + } + else + { + digAlgId = new AlgorithmIdentifier((DerObjectIdentifier)digestOids[sigAlgId.Algorithm], DerNull.Instance); + } + + return digAlgId; + } + + public AlgorithmIdentifier find(String digAlgName) + { + return new AlgorithmIdentifier((DerObjectIdentifier)digestNameToOids[digAlgName], DerNull.Instance); + } + } + public class CmsSignedGenerator { /** @@ -29,233 +123,145 @@ namespace Org.BouncyCastle.Cms */ public static readonly string Data = CmsObjectIdentifiers.Data.Id; - public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id; + public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id; public static readonly string DigestSha224 = NistObjectIdentifiers.IdSha224.Id; public static readonly string DigestSha256 = NistObjectIdentifiers.IdSha256.Id; public static readonly string DigestSha384 = NistObjectIdentifiers.IdSha384.Id; public static readonly string DigestSha512 = NistObjectIdentifiers.IdSha512.Id; public static readonly string DigestMD5 = PkcsObjectIdentifiers.MD5.Id; public static readonly string DigestGost3411 = CryptoProObjectIdentifiers.GostR3411.Id; - public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id; - public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id; - public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id; + public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id; + public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id; + public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id; - public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id; + public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id; public static readonly string EncryptionDsa = X9ObjectIdentifiers.IdDsaWithSha1.Id; public static readonly string EncryptionECDsa = X9ObjectIdentifiers.ECDsaWithSha1.Id; public static readonly string EncryptionRsaPss = PkcsObjectIdentifiers.IdRsassaPss.Id; public static readonly string EncryptionGost3410 = CryptoProObjectIdentifiers.GostR3410x94.Id; public static readonly string EncryptionECGost3410 = CryptoProObjectIdentifiers.GostR3410x2001.Id; - private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id; - private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id; - private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id; - private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id; - private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id; - - private static readonly ISet noParams = new HashSet(); - private static readonly IDictionary ecAlgorithms = Platform.CreateHashtable(); - - static CmsSignedGenerator() - { - noParams.Add(EncryptionDsa); -// noParams.Add(EncryptionECDsa); - noParams.Add(EncryptionECDsaWithSha1); - noParams.Add(EncryptionECDsaWithSha224); - noParams.Add(EncryptionECDsaWithSha256); - noParams.Add(EncryptionECDsaWithSha384); - noParams.Add(EncryptionECDsaWithSha512); - - ecAlgorithms.Add(DigestSha1, EncryptionECDsaWithSha1); - ecAlgorithms.Add(DigestSha224, EncryptionECDsaWithSha224); - ecAlgorithms.Add(DigestSha256, EncryptionECDsaWithSha256); - ecAlgorithms.Add(DigestSha384, EncryptionECDsaWithSha384); - ecAlgorithms.Add(DigestSha512, EncryptionECDsaWithSha512); - } - - internal IList _certs = Platform.CreateArrayList(); + internal IList _certs = Platform.CreateArrayList(); internal IList _crls = Platform.CreateArrayList(); - internal IList _signers = Platform.CreateArrayList(); - internal IDictionary _digests = Platform.CreateHashtable(); - - protected readonly SecureRandom rand; - - protected CmsSignedGenerator() - : this(new SecureRandom()) - { - } - - /// <summary>Constructor allowing specific source of randomness</summary> - /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> - protected CmsSignedGenerator( - SecureRandom rand) - { - this.rand = rand; - } - - protected string GetEncOid( - AsymmetricKeyParameter key, - string digestOID) + internal IList _signers = Platform.CreateArrayList(); + internal IDictionary _digests = Platform.CreateHashtable(); + + protected readonly SecureRandom rand; + + protected CmsSignedGenerator() + : this(new SecureRandom()) { - string encOID = null; - - if (key is RsaKeyParameters) - { - if (!((RsaKeyParameters) key).IsPrivate) - throw new ArgumentException("Expected RSA private key"); - - encOID = EncryptionRsa; - } - else if (key is DsaPrivateKeyParameters) - { - if (!digestOID.Equals(DigestSha1)) - throw new ArgumentException("can't mix DSA with anything but SHA1"); - - encOID = EncryptionDsa; - } - else if (key is ECPrivateKeyParameters) - { - ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters) key; - string algName = ecPrivKey.AlgorithmName; - - if (algName == "ECGOST3410") - { - encOID = EncryptionECGost3410; - } - else - { - // TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does? - encOID = (string) ecAlgorithms[digestOID]; - - if (encOID == null) - throw new ArgumentException("can't mix ECDSA with anything but SHA family digests"); - } - } - else if (key is Gost3410PrivateKeyParameters) - { - encOID = EncryptionGost3410; - } - else - { - throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid"); - } - - return encOID; } - internal static AlgorithmIdentifier GetEncAlgorithmIdentifier( - DerObjectIdentifier encOid, - Asn1Encodable sigX509Parameters) - { - if (noParams.Contains(encOid.Id)) - { - return new AlgorithmIdentifier(encOid); - } - - return new AlgorithmIdentifier(encOid, sigX509Parameters); - } - - internal protected virtual IDictionary GetBaseParameters( - DerObjectIdentifier contentType, - AlgorithmIdentifier digAlgId, - byte[] hash) - { - IDictionary param = Platform.CreateHashtable(); + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + protected CmsSignedGenerator( + SecureRandom rand) + { + this.rand = rand; + } + + internal protected virtual IDictionary GetBaseParameters( + DerObjectIdentifier contentType, + AlgorithmIdentifier digAlgId, + byte[] hash) + { + IDictionary param = Platform.CreateHashtable(); if (contentType != null) { param[CmsAttributeTableParameter.ContentType] = contentType; } - param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId; + param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId; param[CmsAttributeTableParameter.Digest] = hash.Clone(); return param; - } + } - internal protected virtual Asn1Set GetAttributeSet( + internal protected virtual Asn1Set GetAttributeSet( Asn1.Cms.AttributeTable attr) { - return attr == null - ? null - : new DerSet(attr.ToAsn1EncodableVector()); + return attr == null + ? null + : new DerSet(attr.ToAsn1EncodableVector()); } - public void AddCertificates( - IX509Store certStore) - { + public void AddCertificates( + IX509Store certStore) + { CollectionUtilities.AddRange(_certs, CmsUtilities.GetCertificatesFromStore(certStore)); } - public void AddCrls( - IX509Store crlStore) - { + public void AddCrls( + IX509Store crlStore) + { CollectionUtilities.AddRange(_crls, CmsUtilities.GetCrlsFromStore(crlStore)); - } + } - /** + /** * Add the attribute certificates contained in the passed in store to the * generator. * * @param store a store of Version 2 attribute certificates * @throws CmsException if an error occurse processing the store. */ - public void AddAttributeCertificates( - IX509Store store) - { - try - { - foreach (IX509AttributeCertificate attrCert in store.GetMatches(null)) - { - _certs.Add(new DerTaggedObject(false, 2, - AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded())))); - } - } - catch (Exception e) - { - throw new CmsException("error processing attribute certs", e); - } - } - - /** + public void AddAttributeCertificates( + IX509Store store) + { + try + { + foreach (IX509AttributeCertificate attrCert in store.GetMatches(null)) + { + _certs.Add(new DerTaggedObject(false, 2, + AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded())))); + } + } + catch (Exception e) + { + throw new CmsException("error processing attribute certs", e); + } + } + + /** * Add a store of precalculated signers to the generator. * * @param signerStore store of signers */ - public void AddSigners( - SignerInformationStore signerStore) - { - foreach (SignerInformation o in signerStore.GetSigners()) - { - _signers.Add(o); - AddSignerCallback(o); - } - } - - /** + public void AddSigners( + SignerInformationStore signerStore) + { + foreach (SignerInformation o in signerStore.GetSigners()) + { + _signers.Add(o); + AddSignerCallback(o); + } + } + + /** * Return a map of oids and byte arrays representing the digests calculated on the content during * the last generate. * * @return a map of oids (as String objects) and byte[] representing digests. */ - public IDictionary GetGeneratedDigests() - { - return Platform.CreateHashtable(_digests); - } - - internal virtual void AddSignerCallback( - SignerInformation si) - { - } - - internal static SignerIdentifier GetSignerIdentifier(X509Certificate cert) - { - return new SignerIdentifier(CmsUtilities.GetIssuerAndSerialNumber(cert)); - } - - internal static SignerIdentifier GetSignerIdentifier(byte[] subjectKeyIdentifier) - { - return new SignerIdentifier(new DerOctetString(subjectKeyIdentifier)); - } - } + public IDictionary GetGeneratedDigests() + { + return Platform.CreateHashtable(_digests); + } + + internal virtual void AddSignerCallback( + SignerInformation si) + { + } + + internal static SignerIdentifier GetSignerIdentifier(X509Certificate cert) + { + return new SignerIdentifier(CmsUtilities.GetIssuerAndSerialNumber(cert)); + } + + internal static SignerIdentifier GetSignerIdentifier(byte[] subjectKeyIdentifier) + { + return new SignerIdentifier(new DerOctetString(subjectKeyIdentifier)); + } + } } diff --git a/crypto/src/cms/CMSSignedHelper.cs b/crypto/src/cms/CMSSignedHelper.cs
index b3406fc06..23657ef86 100644 --- a/crypto/src/cms/CMSSignedHelper.cs +++ b/crypto/src/cms/CMSSignedHelper.cs
@@ -18,6 +18,8 @@ using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.X509; using Org.BouncyCastle.X509.Store; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Cms { @@ -25,11 +27,20 @@ namespace Org.BouncyCastle.Cms { internal static readonly CmsSignedHelper Instance = new CmsSignedHelper(); - private static readonly IDictionary encryptionAlgs = Platform.CreateHashtable(); + private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id; + private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id; + private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id; + private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id; + private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id; + + private static readonly IDictionary encryptionAlgs = Platform.CreateHashtable(); private static readonly IDictionary digestAlgs = Platform.CreateHashtable(); private static readonly IDictionary digestAliases = Platform.CreateHashtable(); - private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption) + private static readonly ISet noParams = new HashSet(); + private static readonly IDictionary ecAlgorithms = Platform.CreateHashtable(); + + private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption) { string alias = oid.Id; digestAlgs.Add(alias, digest); @@ -100,7 +111,21 @@ namespace Org.BouncyCastle.Cms digestAliases.Add("SHA256", new string[] { "SHA-256" }); digestAliases.Add("SHA384", new string[] { "SHA-384" }); digestAliases.Add("SHA512", new string[] { "SHA-512" }); - } + + noParams.Add(CmsSignedGenerator.EncryptionDsa); + // noParams.Add(EncryptionECDsa); + noParams.Add(EncryptionECDsaWithSha1); + noParams.Add(EncryptionECDsaWithSha224); + noParams.Add(EncryptionECDsaWithSha256); + noParams.Add(EncryptionECDsaWithSha384); + noParams.Add(EncryptionECDsaWithSha512); + + ecAlgorithms.Add(CmsSignedGenerator.DigestSha1, EncryptionECDsaWithSha1); + ecAlgorithms.Add(CmsSignedGenerator.DigestSha224, EncryptionECDsaWithSha224); + ecAlgorithms.Add(CmsSignedGenerator.DigestSha256, EncryptionECDsaWithSha256); + ecAlgorithms.Add(CmsSignedGenerator.DigestSha384, EncryptionECDsaWithSha384); + ecAlgorithms.Add(CmsSignedGenerator.DigestSha512, EncryptionECDsaWithSha512); + } /** * Return the digest algorithm using one of the standard JCA string @@ -119,7 +144,19 @@ namespace Org.BouncyCastle.Cms return digestAlgOid; } - internal string[] GetDigestAliases( + internal AlgorithmIdentifier GetEncAlgorithmIdentifier( + DerObjectIdentifier encOid, + Asn1Encodable sigX509Parameters) + { + if (noParams.Contains(encOid.Id)) + { + return new AlgorithmIdentifier(encOid); + } + + return new AlgorithmIdentifier(encOid, sigX509Parameters); + } + + internal string[] GetDigestAliases( string algName) { string[] aliases = (string[]) digestAliases[algName]; @@ -315,5 +352,75 @@ namespace Org.BouncyCastle.Cms return algId; } + + internal string GetEncOid( + AsymmetricKeyParameter key, + string digestOID) + { + string encOID = null; + + if (key is RsaKeyParameters) + { + if (!((RsaKeyParameters)key).IsPrivate) + throw new ArgumentException("Expected RSA private key"); + + encOID = CmsSignedGenerator.EncryptionRsa; + } + else if (key is DsaPrivateKeyParameters) + { + if (digestOID.Equals(CmsSignedGenerator.DigestSha1)) + { + encOID = CmsSignedGenerator.EncryptionDsa; + } + else if (digestOID.Equals(CmsSignedGenerator.DigestSha224)) + { + encOID = NistObjectIdentifiers.DsaWithSha224.Id; + } + else if (digestOID.Equals(CmsSignedGenerator.DigestSha256)) + { + encOID = NistObjectIdentifiers.DsaWithSha256.Id; + } + else if (digestOID.Equals(CmsSignedGenerator.DigestSha384)) + { + encOID = NistObjectIdentifiers.DsaWithSha384.Id; + } + else if (digestOID.Equals(CmsSignedGenerator.DigestSha512)) + { + encOID = NistObjectIdentifiers.DsaWithSha512.Id; + } + else + { + throw new ArgumentException("can't mix DSA with anything but SHA1/SHA2"); + } + } + else if (key is ECPrivateKeyParameters) + { + ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters)key; + string algName = ecPrivKey.AlgorithmName; + + if (algName == "ECGOST3410") + { + encOID = CmsSignedGenerator.EncryptionECGost3410; + } + else + { + // TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does? + encOID = (string)ecAlgorithms[digestOID]; + + if (encOID == null) + throw new ArgumentException("can't mix ECDSA with anything but SHA family digests"); + } + } + else if (key is Gost3410PrivateKeyParameters) + { + encOID = CmsSignedGenerator.EncryptionGost3410; + } + else + { + throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid"); + } + + return encOID; + } } } diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs
index 9cb314211..d04846ee1 100644 --- a/crypto/src/cms/CMSTypedStream.cs +++ b/crypto/src/cms/CMSTypedStream.cs
@@ -2,7 +2,6 @@ using System; using System.IO; using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.Utilities; using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Cms diff --git a/crypto/src/cms/KeyAgreeRecipientInformation.cs b/crypto/src/cms/KeyAgreeRecipientInformation.cs
index 38a94b0a4..8e006e545 100644 --- a/crypto/src/cms/KeyAgreeRecipientInformation.cs +++ b/crypto/src/cms/KeyAgreeRecipientInformation.cs
@@ -18,209 +18,209 @@ using Org.BouncyCastle.X509; namespace Org.BouncyCastle.Cms { - /** - * the RecipientInfo class for a recipient who has been sent a message - * encrypted using key agreement. - */ - public class KeyAgreeRecipientInformation - : RecipientInformation - { - private KeyAgreeRecipientInfo info; - private Asn1OctetString encryptedKey; - - internal static void ReadRecipientInfo(IList infos, KeyAgreeRecipientInfo info, - CmsSecureReadable secureReadable) - { - try - { - foreach (Asn1Encodable rek in info.RecipientEncryptedKeys) - { - RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(rek.ToAsn1Object()); - - RecipientID rid = new RecipientID(); - - Asn1.Cms.KeyAgreeRecipientIdentifier karid = id.Identifier; - - Asn1.Cms.IssuerAndSerialNumber iAndSN = karid.IssuerAndSerialNumber; - if (iAndSN != null) - { - rid.Issuer = iAndSN.Name; - rid.SerialNumber = iAndSN.SerialNumber.Value; - } - else - { - Asn1.Cms.RecipientKeyIdentifier rKeyID = karid.RKeyID; - - // Note: 'date' and 'other' fields of RecipientKeyIdentifier appear to be only informational - - rid.SubjectKeyIdentifier = rKeyID.SubjectKeyIdentifier.GetOctets(); - } - - infos.Add(new KeyAgreeRecipientInformation(info, rid, id.EncryptedKey, - secureReadable)); - } - } - catch (IOException e) - { - throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e); - } - } - - internal KeyAgreeRecipientInformation( - KeyAgreeRecipientInfo info, - RecipientID rid, - Asn1OctetString encryptedKey, - CmsSecureReadable secureReadable) - : base(info.KeyEncryptionAlgorithm, secureReadable) - { - this.info = info; - this.rid = rid; - this.encryptedKey = encryptedKey; - } - - private AsymmetricKeyParameter GetSenderPublicKey( - AsymmetricKeyParameter receiverPrivateKey, - OriginatorIdentifierOrKey originator) - { - OriginatorPublicKey opk = originator.OriginatorPublicKey; - if (opk != null) - { - return GetPublicKeyFromOriginatorPublicKey(receiverPrivateKey, opk); - } - - OriginatorID origID = new OriginatorID(); - - Asn1.Cms.IssuerAndSerialNumber iAndSN = originator.IssuerAndSerialNumber; - if (iAndSN != null) - { - origID.Issuer = iAndSN.Name; - origID.SerialNumber = iAndSN.SerialNumber.Value; - } - else - { - SubjectKeyIdentifier ski = originator.SubjectKeyIdentifier; - - origID.SubjectKeyIdentifier = ski.GetKeyIdentifier(); - } - - return GetPublicKeyFromOriginatorID(origID); - } - - private AsymmetricKeyParameter GetPublicKeyFromOriginatorPublicKey( - AsymmetricKeyParameter receiverPrivateKey, - OriginatorPublicKey originatorPublicKey) - { - PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(receiverPrivateKey); - SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo( - privInfo.AlgorithmID, - originatorPublicKey.PublicKey.GetBytes()); - return PublicKeyFactory.CreateKey(pubInfo); - } - - private AsymmetricKeyParameter GetPublicKeyFromOriginatorID( - OriginatorID origID) - { - // TODO Support all alternatives for OriginatorIdentifierOrKey - // see RFC 3852 6.2.2 - throw new CmsException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier"); - } - - private KeyParameter CalculateAgreedWrapKey( - string wrapAlg, - AsymmetricKeyParameter senderPublicKey, - AsymmetricKeyParameter receiverPrivateKey) - { - DerObjectIdentifier agreeAlgID = keyEncAlg.ObjectID; - - ICipherParameters senderPublicParams = senderPublicKey; - ICipherParameters receiverPrivateParams = receiverPrivateKey; - - if (agreeAlgID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf)) - { - byte[] ukmEncoding = info.UserKeyingMaterial.GetOctets(); - MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.GetInstance( - Asn1Object.FromByteArray(ukmEncoding)); - - AsymmetricKeyParameter ephemeralKey = GetPublicKeyFromOriginatorPublicKey( - receiverPrivateKey, ukm.EphemeralPublicKey); - - senderPublicParams = new MqvPublicParameters( - (ECPublicKeyParameters)senderPublicParams, - (ECPublicKeyParameters)ephemeralKey); - receiverPrivateParams = new MqvPrivateParameters( - (ECPrivateKeyParameters)receiverPrivateParams, - (ECPrivateKeyParameters)receiverPrivateParams); - } - - IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf( - agreeAlgID, wrapAlg); - agreement.Init(receiverPrivateParams); - BigInteger agreedValue = agreement.CalculateAgreement(senderPublicParams); - - int wrapKeySize = GeneratorUtilities.GetDefaultKeySize(wrapAlg) / 8; - byte[] wrapKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, wrapKeySize); - return ParameterUtilities.CreateKeyParameter(wrapAlg, wrapKeyBytes); - } - - private KeyParameter UnwrapSessionKey( - string wrapAlg, - KeyParameter agreedKey) - { - byte[] encKeyOctets = encryptedKey.GetOctets(); - - IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg); - keyCipher.Init(false, agreedKey); - byte[] sKeyBytes = keyCipher.Unwrap(encKeyOctets, 0, encKeyOctets.Length); - return ParameterUtilities.CreateKeyParameter(GetContentAlgorithmName(), sKeyBytes); - } - - internal KeyParameter GetSessionKey( - AsymmetricKeyParameter receiverPrivateKey) - { - try - { - string wrapAlg = DerObjectIdentifier.GetInstance( - Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id; - - AsymmetricKeyParameter senderPublicKey = GetSenderPublicKey( - receiverPrivateKey, info.Originator); - - KeyParameter agreedWrapKey = CalculateAgreedWrapKey(wrapAlg, - senderPublicKey, receiverPrivateKey); - - return UnwrapSessionKey(wrapAlg, agreedWrapKey); - } - catch (SecurityUtilityException e) - { - throw new CmsException("couldn't create cipher.", e); - } - catch (InvalidKeyException e) - { - throw new CmsException("key invalid in message.", e); - } - catch (Exception e) - { - throw new CmsException("originator key invalid.", e); - } - } - - /** - * decrypt the content and return an input stream. - */ - public override CmsTypedStream GetContentStream( - ICipherParameters key) - { - if (!(key is AsymmetricKeyParameter)) - throw new ArgumentException("KeyAgreement requires asymmetric key", "key"); - - AsymmetricKeyParameter receiverPrivateKey = (AsymmetricKeyParameter) key; - - if (!receiverPrivateKey.IsPrivate) - throw new ArgumentException("Expected private key", "key"); - - KeyParameter sKey = GetSessionKey(receiverPrivateKey); - - return GetContentFromSessionKey(sKey); - } - } + /** + * the RecipientInfo class for a recipient who has been sent a message + * encrypted using key agreement. + */ + public class KeyAgreeRecipientInformation + : RecipientInformation + { + private KeyAgreeRecipientInfo info; + private Asn1OctetString encryptedKey; + + internal static void ReadRecipientInfo(IList infos, KeyAgreeRecipientInfo info, + CmsSecureReadable secureReadable) + { + try + { + foreach (Asn1Encodable rek in info.RecipientEncryptedKeys) + { + RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(rek.ToAsn1Object()); + + RecipientID rid = new RecipientID(); + + Asn1.Cms.KeyAgreeRecipientIdentifier karid = id.Identifier; + + Asn1.Cms.IssuerAndSerialNumber iAndSN = karid.IssuerAndSerialNumber; + if (iAndSN != null) + { + rid.Issuer = iAndSN.Name; + rid.SerialNumber = iAndSN.SerialNumber.Value; + } + else + { + Asn1.Cms.RecipientKeyIdentifier rKeyID = karid.RKeyID; + + // Note: 'date' and 'other' fields of RecipientKeyIdentifier appear to be only informational + + rid.SubjectKeyIdentifier = rKeyID.SubjectKeyIdentifier.GetOctets(); + } + + infos.Add(new KeyAgreeRecipientInformation(info, rid, id.EncryptedKey, + secureReadable)); + } + } + catch (IOException e) + { + throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e); + } + } + + internal KeyAgreeRecipientInformation( + KeyAgreeRecipientInfo info, + RecipientID rid, + Asn1OctetString encryptedKey, + CmsSecureReadable secureReadable) + : base(info.KeyEncryptionAlgorithm, secureReadable) + { + this.info = info; + this.rid = rid; + this.encryptedKey = encryptedKey; + } + + private AsymmetricKeyParameter GetSenderPublicKey( + AsymmetricKeyParameter receiverPrivateKey, + OriginatorIdentifierOrKey originator) + { + OriginatorPublicKey opk = originator.OriginatorPublicKey; + if (opk != null) + { + return GetPublicKeyFromOriginatorPublicKey(receiverPrivateKey, opk); + } + + OriginatorID origID = new OriginatorID(); + + Asn1.Cms.IssuerAndSerialNumber iAndSN = originator.IssuerAndSerialNumber; + if (iAndSN != null) + { + origID.Issuer = iAndSN.Name; + origID.SerialNumber = iAndSN.SerialNumber.Value; + } + else + { + SubjectKeyIdentifier ski = originator.SubjectKeyIdentifier; + + origID.SubjectKeyIdentifier = ski.GetKeyIdentifier(); + } + + return GetPublicKeyFromOriginatorID(origID); + } + + private AsymmetricKeyParameter GetPublicKeyFromOriginatorPublicKey( + AsymmetricKeyParameter receiverPrivateKey, + OriginatorPublicKey originatorPublicKey) + { + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(receiverPrivateKey); + SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo( + privInfo.PrivateKeyAlgorithm, + originatorPublicKey.PublicKey.GetBytes()); + return PublicKeyFactory.CreateKey(pubInfo); + } + + private AsymmetricKeyParameter GetPublicKeyFromOriginatorID( + OriginatorID origID) + { + // TODO Support all alternatives for OriginatorIdentifierOrKey + // see RFC 3852 6.2.2 + throw new CmsException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier"); + } + + private KeyParameter CalculateAgreedWrapKey( + string wrapAlg, + AsymmetricKeyParameter senderPublicKey, + AsymmetricKeyParameter receiverPrivateKey) + { + DerObjectIdentifier agreeAlgID = keyEncAlg.ObjectID; + + ICipherParameters senderPublicParams = senderPublicKey; + ICipherParameters receiverPrivateParams = receiverPrivateKey; + + if (agreeAlgID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf)) + { + byte[] ukmEncoding = info.UserKeyingMaterial.GetOctets(); + MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.GetInstance( + Asn1Object.FromByteArray(ukmEncoding)); + + AsymmetricKeyParameter ephemeralKey = GetPublicKeyFromOriginatorPublicKey( + receiverPrivateKey, ukm.EphemeralPublicKey); + + senderPublicParams = new MqvPublicParameters( + (ECPublicKeyParameters)senderPublicParams, + (ECPublicKeyParameters)ephemeralKey); + receiverPrivateParams = new MqvPrivateParameters( + (ECPrivateKeyParameters)receiverPrivateParams, + (ECPrivateKeyParameters)receiverPrivateParams); + } + + IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf( + agreeAlgID, wrapAlg); + agreement.Init(receiverPrivateParams); + BigInteger agreedValue = agreement.CalculateAgreement(senderPublicParams); + + int wrapKeySize = GeneratorUtilities.GetDefaultKeySize(wrapAlg) / 8; + byte[] wrapKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, wrapKeySize); + return ParameterUtilities.CreateKeyParameter(wrapAlg, wrapKeyBytes); + } + + private KeyParameter UnwrapSessionKey( + string wrapAlg, + KeyParameter agreedKey) + { + byte[] encKeyOctets = encryptedKey.GetOctets(); + + IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg); + keyCipher.Init(false, agreedKey); + byte[] sKeyBytes = keyCipher.Unwrap(encKeyOctets, 0, encKeyOctets.Length); + return ParameterUtilities.CreateKeyParameter(GetContentAlgorithmName(), sKeyBytes); + } + + internal KeyParameter GetSessionKey( + AsymmetricKeyParameter receiverPrivateKey) + { + try + { + string wrapAlg = DerObjectIdentifier.GetInstance( + Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id; + + AsymmetricKeyParameter senderPublicKey = GetSenderPublicKey( + receiverPrivateKey, info.Originator); + + KeyParameter agreedWrapKey = CalculateAgreedWrapKey(wrapAlg, + senderPublicKey, receiverPrivateKey); + + return UnwrapSessionKey(wrapAlg, agreedWrapKey); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (Exception e) + { + throw new CmsException("originator key invalid.", e); + } + } + + /** + * decrypt the content and return an input stream. + */ + public override CmsTypedStream GetContentStream( + ICipherParameters key) + { + if (!(key is AsymmetricKeyParameter)) + throw new ArgumentException("KeyAgreement requires asymmetric key", "key"); + + AsymmetricKeyParameter receiverPrivateKey = (AsymmetricKeyParameter) key; + + if (!receiverPrivateKey.IsPrivate) + throw new ArgumentException("Expected private key", "key"); + + KeyParameter sKey = GetSessionKey(receiverPrivateKey); + + return GetContentFromSessionKey(sKey); + } + } } diff --git a/crypto/src/cms/SignerInfoGenerator.cs b/crypto/src/cms/SignerInfoGenerator.cs
index f78cf2c01..62db40ad8 100644 --- a/crypto/src/cms/SignerInfoGenerator.cs +++ b/crypto/src/cms/SignerInfoGenerator.cs
@@ -3,12 +3,165 @@ using System; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cms; using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.X509; namespace Org.BouncyCastle.Cms { - internal interface SignerInfoGenerator - { - SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm, - byte[] calculatedDigest); - } + internal interface ISignerInfoGenerator + { + SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm, + byte[] calculatedDigest); + } + + public class SignerInfoGenerator + { + internal X509Certificate certificate; + internal ISignatureCalculator contentSigner; + internal SignerIdentifier sigId; + internal CmsAttributeTableGenerator signedGen; + internal CmsAttributeTableGenerator unsignedGen; + private bool isDirectSignature; + + internal SignerInfoGenerator(SignerIdentifier sigId, ISignatureCalculator contentSigner): this(sigId, contentSigner, false) + { + + } + + internal SignerInfoGenerator(SignerIdentifier sigId, ISignatureCalculator contentSigner, bool isDirectSignature) + { + this.sigId = sigId; + this.contentSigner = contentSigner; + this.isDirectSignature = isDirectSignature; + if (this.isDirectSignature) + { + this.signedGen = null; + this.unsignedGen = null; + } + else + { + this.signedGen = new DefaultSignedAttributeTableGenerator(); + this.unsignedGen = null; + } + } + + internal SignerInfoGenerator(SignerIdentifier sigId, ISignatureCalculator contentSigner, CmsAttributeTableGenerator signedGen, CmsAttributeTableGenerator unsignedGen) + { + this.sigId = sigId; + this.contentSigner = contentSigner; + this.signedGen = signedGen; + this.unsignedGen = unsignedGen; + this.isDirectSignature = false; + } + + internal void setAssociatedCertificate(X509Certificate certificate) + { + this.certificate = certificate; + } + } + + public class SignerInfoGeneratorBuilder + { + private bool directSignature; + private CmsAttributeTableGenerator signedGen; + private CmsAttributeTableGenerator unsignedGen; + + public SignerInfoGeneratorBuilder() + { + } + + /** + * If the passed in flag is true, the signer signature will be based on the data, not + * a collection of signed attributes, and no signed attributes will be included. + * + * @return the builder object + */ + public SignerInfoGeneratorBuilder SetDirectSignature(bool hasNoSignedAttributes) + { + this.directSignature = hasNoSignedAttributes; + + return this; + } + + /** + * Provide a custom signed attribute generator. + * + * @param signedGen a generator of signed attributes. + * @return the builder object + */ + public SignerInfoGeneratorBuilder WithSignedAttributeGenerator(CmsAttributeTableGenerator signedGen) + { + this.signedGen = signedGen; + + return this; + } + + /** + * Provide a generator of unsigned attributes. + * + * @param unsignedGen a generator for signed attributes. + * @return the builder object + */ + public SignerInfoGeneratorBuilder WithUnsignedAttributeGenerator(CmsAttributeTableGenerator unsignedGen) + { + this.unsignedGen = unsignedGen; + + return this; + } + + /** + * Build a generator with the passed in certHolder issuer and serial number as the signerIdentifier. + * + * @param contentSigner operator for generating the final signature in the SignerInfo with. + * @param certHolder carrier for the X.509 certificate related to the contentSigner. + * @return a SignerInfoGenerator + * @throws OperatorCreationException if the generator cannot be built. + */ + public SignerInfoGenerator Build(ISignatureCalculator contentSigner, X509Certificate certificate) + { + SignerIdentifier sigId = new SignerIdentifier(new IssuerAndSerialNumber(certificate.IssuerDN, new DerInteger(certificate.SerialNumber))); + + SignerInfoGenerator sigInfoGen = CreateGenerator(contentSigner, sigId); + + sigInfoGen.setAssociatedCertificate(certificate); + + return sigInfoGen; + } + + /** + * Build a generator with the passed in subjectKeyIdentifier as the signerIdentifier. If used you should + * try to follow the calculation described in RFC 5280 section 4.2.1.2. + * + * @param contentSigner operator for generating the final signature in the SignerInfo with. + * @param subjectKeyIdentifier key identifier to identify the public key for verifying the signature. + * @return a SignerInfoGenerator + * @throws OperatorCreationException if the generator cannot be built. + */ + public SignerInfoGenerator Build(ISignatureCalculator contentSigner, byte[] subjectKeyIdentifier) + { + SignerIdentifier sigId = new SignerIdentifier(new DerOctetString(subjectKeyIdentifier)); + + return CreateGenerator(contentSigner, sigId); + } + + private SignerInfoGenerator CreateGenerator(ISignatureCalculator contentSigner, SignerIdentifier sigId) + { + if (directSignature) + { + return new SignerInfoGenerator(sigId, contentSigner, true); + } + + if (signedGen != null || unsignedGen != null) + { + if (signedGen == null) + { + signedGen = new DefaultSignedAttributeTableGenerator(); + } + + return new SignerInfoGenerator(sigId, contentSigner, signedGen, unsignedGen); + } + + return new SignerInfoGenerator(sigId, contentSigner); + } + } } diff --git a/crypto/src/cms/SignerInformation.cs b/crypto/src/cms/SignerInformation.cs
index 20af29a50..581286a3f 100644 --- a/crypto/src/cms/SignerInformation.cs +++ b/crypto/src/cms/SignerInformation.cs
@@ -345,9 +345,12 @@ namespace Org.BouncyCastle.Cms // if (sigParams != null) // throw new CmsException("unrecognised signature parameters provided"); - string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(this.EncryptionAlgOid); + string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(this.EncryptionAlgOid); - sig = Helper.GetSignatureInstance(signatureName); + sig = Helper.GetSignatureInstance(signatureName); + + //sig = Helper.GetSignatureInstance(this.EncryptionAlgOid); + //sig = SignerUtilities.GetSigner(sigAlgOid); } try diff --git a/crypto/src/cms/SignerInformationStore.cs b/crypto/src/cms/SignerInformationStore.cs
index bd613843d..27940865d 100644 --- a/crypto/src/cms/SignerInformationStore.cs +++ b/crypto/src/cms/SignerInformationStore.cs
@@ -8,10 +8,31 @@ namespace Org.BouncyCastle.Cms { public class SignerInformationStore { - private readonly IList all; //ArrayList[SignerInformation] - private readonly IDictionary table = Platform.CreateHashtable(); // Hashtable[SignerID, ArrayList[SignerInformation]] + private readonly IList all; //ArrayList[SignerInformation] + private readonly IDictionary table = Platform.CreateHashtable(); // Hashtable[SignerID, ArrayList[SignerInformation]] - public SignerInformationStore( + /** + * Create a store containing a single SignerInformation object. + * + * @param signerInfo the signer information to contain. + */ + public SignerInformationStore( + SignerInformation signerInfo) + { + this.all = Platform.CreateArrayList(1); + this.all.Add(signerInfo); + + SignerID sid = signerInfo.SignerID; + + table[sid] = all; + } + + /** + * Create a store containing a collection of SignerInformation objects. + * + * @param signerInfos a collection signer information objects to contain. + */ + public SignerInformationStore( ICollection signerInfos) { foreach (SignerInformation signer in signerInfos) @@ -19,12 +40,12 @@ namespace Org.BouncyCastle.Cms SignerID sid = signer.SignerID; IList list = (IList)table[sid]; - if (list == null) - { - table[sid] = list = Platform.CreateArrayList(1); - } + if (list == null) + { + table[sid] = list = Platform.CreateArrayList(1); + } - list.Add(signer); + list.Add(signer); } this.all = Platform.CreateArrayList(signerInfos); @@ -40,24 +61,24 @@ namespace Org.BouncyCastle.Cms public SignerInformation GetFirstSigner( SignerID selector) { - IList list = (IList) table[selector]; + IList list = (IList) table[selector]; - return list == null ? null : (SignerInformation) list[0]; + return list == null ? null : (SignerInformation) list[0]; } - /// <summary>The number of signers in the collection.</summary> - public int Count + /// <summary>The number of signers in the collection.</summary> + public int Count { - get { return all.Count; } + get { return all.Count; } } - /// <returns>An ICollection of all signers in the collection</returns> + /// <returns>An ICollection of all signers in the collection</returns> public ICollection GetSigners() { return Platform.CreateArrayList(all); } - /** + /** * Return possible empty collection with signers matching the passed in SignerID * * @param selector a signer id to select against. @@ -66,7 +87,7 @@ namespace Org.BouncyCastle.Cms public ICollection GetSigners( SignerID selector) { - IList list = (IList) table[selector]; + IList list = (IList) table[selector]; return list == null ? Platform.CreateArrayList() : Platform.CreateArrayList(list); } diff --git a/crypto/src/crypto/BufferedAeadBlockCipher.cs b/crypto/src/crypto/BufferedAeadBlockCipher.cs
index 04bcb88ed..7ba41090f 100644 --- a/crypto/src/crypto/BufferedAeadBlockCipher.cs +++ b/crypto/src/crypto/BufferedAeadBlockCipher.cs
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Crypto } /** - * process a single byte, producing an output block if neccessary. + * process a single byte, producing an output block if necessary. * * @param in the input byte. * @param out the space for any output that might be produced. diff --git a/crypto/src/crypto/BufferedBlockCipher.cs b/crypto/src/crypto/BufferedBlockCipher.cs
index 3a98798a2..c87d2daf9 100644 --- a/crypto/src/crypto/BufferedBlockCipher.cs +++ b/crypto/src/crypto/BufferedBlockCipher.cs
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Crypto } /** - * process a single byte, producing an output block if neccessary. + * process a single byte, producing an output block if necessary. * * @param in the input byte. * @param out the space for any output that might be produced. @@ -223,13 +223,10 @@ namespace Org.BouncyCastle.Crypto if (outLength > 0) { - if ((outOff + outLength) > output.Length) - { - throw new DataLengthException("output buffer too short"); - } + Check.OutputLength(output, outOff, outLength, "output buffer too short"); } - int resultLen = 0; + int resultLen = 0; int gapLen = buf.Length - bufOff; if (length > gapLen) { @@ -339,17 +336,10 @@ namespace Org.BouncyCastle.Crypto { 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 + Check.DataLength(!cipher.IsPartialBlockOkay, "data not block size aligned"); + Check.OutputLength(output, outOff, bufOff, "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); } diff --git a/crypto/src/crypto/Check.cs b/crypto/src/crypto/Check.cs new file mode 100644
index 000000000..96a05c64b --- /dev/null +++ b/crypto/src/crypto/Check.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + internal class Check + { + internal static void DataLength(bool condition, string msg) + { + if (condition) + throw new DataLengthException(msg); + } + + internal static void DataLength(byte[] buf, int off, int len, string msg) + { + if (off + len > buf.Length) + throw new DataLengthException(msg); + } + + internal static void OutputLength(byte[] buf, int off, int len, string msg) + { + if (off + len > buf.Length) + throw new OutputLengthException(msg); + } + } +} diff --git a/crypto/src/crypto/IBlockResult.cs b/crypto/src/crypto/IBlockResult.cs new file mode 100644
index 000000000..c12bdaa1d --- /dev/null +++ b/crypto/src/crypto/IBlockResult.cs
@@ -0,0 +1,24 @@ + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Operators that reduce their input to a single block return an object + /// of this type. + /// </summary> + public interface IBlockResult + { + /// <summary> + /// Return the final result of the operation. + /// </summary> + /// <returns>A block of bytes, representing the result of an operation.</returns> + byte[] DoFinal(); + + /// <summary> + /// Store the final result of the operation by copying it into the destination array. + /// </summary> + /// <returns>The number of bytes copied into destination.</returns> + /// <param name="destination">The byte array to copy the result into.</param> + /// <param name="offset">The offset into destination to start copying the result at.</param> + int DoFinal(byte[] destination, int offset); + } +} diff --git a/crypto/src/crypto/ISignatureCalculator.cs b/crypto/src/crypto/ISignatureCalculator.cs new file mode 100644
index 000000000..bb733818d --- /dev/null +++ b/crypto/src/crypto/ISignatureCalculator.cs
@@ -0,0 +1,23 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Base interface for operators that serve as stream-based signature calculators. + /// </summary> + public interface ISignatureCalculator + { + /// <summary>The algorithm details object for this calculator.</summary> + Object AlgorithmDetails { get ; } + + /// <summary> + /// Create a stream calculator for this signature calculator. The stream + /// calculator is used for the actual operation of entering the data to be signed + /// and producing the signature block. + /// </summary> + /// <returns>A calculator producing an IBlockResult with a signature in it.</returns> + IStreamCalculator CreateCalculator(); + } +} + + diff --git a/crypto/src/crypto/ISignatureVerifier.cs b/crypto/src/crypto/ISignatureVerifier.cs new file mode 100644
index 000000000..1f42a0256 --- /dev/null +++ b/crypto/src/crypto/ISignatureVerifier.cs
@@ -0,0 +1,21 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Base interface for operators that serve as stream-based signature verifiers. + /// </summary> + public interface ISignatureVerifier + { + /// <summary>The algorithm details object for this verifier.</summary> + Object AlgorithmDetails { get ; } + + /// <summary> + /// Create a stream calculator for this verifier. The stream + /// calculator is used for the actual operation of entering the data to be verified + /// and producing a result which can be used to verify the original signature. + /// </summary> + /// <returns>A calculator producing an IVerifier which can verify the signature.</returns> + IStreamCalculator CreateCalculator(); + } +} diff --git a/crypto/src/crypto/ISignatureVerifierProvider.cs b/crypto/src/crypto/ISignatureVerifierProvider.cs new file mode 100644
index 000000000..20180e22a --- /dev/null +++ b/crypto/src/crypto/ISignatureVerifierProvider.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Base interface for a provider to support the dynamic creation of signature verifiers. + /// </summary> + public interface ISignatureVerifierProvider + { + /// <summary> + /// Return a signature verfier for signature algorithm described in the passed in algorithm details object. + /// </summary> + /// <param name="algorithmDetails">The details of the signature algorithm verification is required for.</param> + /// <returns>A new signature verifier.</returns> + ISignatureVerifier CreateSignatureVerifier (Object algorithmDetails); + } +} + diff --git a/crypto/src/crypto/IStreamCalculator.cs b/crypto/src/crypto/IStreamCalculator.cs new file mode 100644
index 000000000..19a542845 --- /dev/null +++ b/crypto/src/crypto/IStreamCalculator.cs
@@ -0,0 +1,23 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Base interface for cryptographic operations such as Hashes, MACs, and Signatures which reduce a stream of data + /// to a single value. + /// </summary> + public interface IStreamCalculator + { + /// <summary>Return a "sink" stream which only exists to update the implementing object.</summary> + /// <returns>A stream to write to in order to update the implementing object.</returns> + Stream Stream { get; } + + /// <summary> + /// Return the result of processing the stream. This value is only available once the stream + /// has been closed. + /// </summary> + /// <returns>The result of processing the stream.</returns> + Object GetResult(); + } +} diff --git a/crypto/src/crypto/IVerifier.cs b/crypto/src/crypto/IVerifier.cs new file mode 100644
index 000000000..560cabf8e --- /dev/null +++ b/crypto/src/crypto/IVerifier.cs
@@ -0,0 +1,25 @@ +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Operators that reduce their input to the validation of a signature produce this type. + /// </summary> + public interface IVerifier + { + /// <summary> + /// Return true if the passed in data matches what is expected by the verification result. + /// </summary> + /// <param name="data">The bytes representing the signature.</param> + /// <returns>true if the signature verifies, false otherwise.</returns> + bool IsVerified(byte[] data); + + /// <summary> + /// Return true if the length bytes from off in the source array match the signature + /// expected by the verification result. + /// </summary> + /// <param name="source">Byte array containing the signature.</param> + /// <param name="off">The offset into the source array where the signature starts.</param> + /// <param name="length">The number of bytes in source making up the signature.</param> + /// <returns>true if the signature verifies, false otherwise.</returns> + bool IsVerified(byte[] source, int off, int length); + } +} diff --git a/crypto/src/crypto/IXof.cs b/crypto/src/crypto/IXof.cs new file mode 100644
index 000000000..e9e2253a0 --- /dev/null +++ b/crypto/src/crypto/IXof.cs
@@ -0,0 +1,22 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <remarks> + /// With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes. + /// This interface provides the extra method required to support variable output on a digest implementation. + /// </remarks> + public interface IXof + : IDigest + { + /** + * Output the results of the final calculation for this digest to outLen number of bytes. + * + * @param out output array to write the output bytes to. + * @param outOff offset to start writing the bytes at. + * @param outLen the number of output bytes requested. + * @return the number of bytes written + */ + int DoFinal(byte[] output, int outOff, int outLen); + } +} diff --git a/crypto/src/crypto/OutputLengthException.cs b/crypto/src/crypto/OutputLengthException.cs new file mode 100644
index 000000000..e1cf44925 --- /dev/null +++ b/crypto/src/crypto/OutputLengthException.cs
@@ -0,0 +1,28 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class OutputLengthException + : DataLengthException + { + public OutputLengthException() + { + } + + public OutputLengthException( + string message) + : base(message) + { + } + + public OutputLengthException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/crypto/src/crypto/agreement/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs new file mode 100644
index 000000000..93b65af98 --- /dev/null +++ b/crypto/src/crypto/agreement/DHStandardGroups.cs
@@ -0,0 +1,206 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /// <summary>Standard Diffie-Hellman groups from various IETF specifications.</summary> + public class DHStandardGroups + { + private static BigInteger FromHex(string hex) + { + return new BigInteger(1, Hex.Decode(hex)); + } + + private static DHParameters FromPG(string hexP, string hexG) + { + return new DHParameters(FromHex(hexP), FromHex(hexG)); + } + + private static DHParameters FromPGQ(string hexP, string hexG, string hexQ) + { + return new DHParameters(FromHex(hexP), FromHex(hexG), FromHex(hexQ)); + } + + /* + * RFC 2409 + */ + private static readonly string rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"; + private static readonly string rfc2409_768_g = "02"; + public static readonly DHParameters rfc2409_768 = FromPG(rfc2409_768_p, rfc2409_768_g); + + private static readonly string rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" + + "FFFFFFFFFFFFFFFF"; + private static readonly string rfc2409_1024_g = "02"; + public static readonly DHParameters rfc2409_1024 = FromPG(rfc2409_1024_p, rfc2409_1024_g); + + /* + * RFC 3526 + */ + private static readonly string rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_1536_g = "02"; + public static readonly DHParameters rfc3526_1536 = FromPG(rfc3526_1536_p, rfc3526_1536_g); + + private static readonly string rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_2048_g = "02"; + public static readonly DHParameters rfc3526_2048 = FromPG(rfc3526_2048_p, rfc3526_2048_g); + + private static readonly string rfc3526_3072_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_3072_g = "02"; + public static readonly DHParameters rfc3526_3072 = FromPG(rfc3526_3072_p, rfc3526_3072_g); + + private static readonly string rfc3526_4096_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_4096_g = "02"; + public static readonly DHParameters rfc3526_4096 = FromPG(rfc3526_4096_p, rfc3526_4096_g); + + private static readonly string rfc3526_6144_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + + "6DCC4024FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_6144_g = "02"; + public static readonly DHParameters rfc3526_6144 = FromPG(rfc3526_6144_p, rfc3526_6144_g); + + private static readonly string rfc3526_8192_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" + + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" + + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" + + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" + + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" + + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" + + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" + + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" + + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" + + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_8192_g = "02"; + public static readonly DHParameters rfc3526_8192 = FromPG(rfc3526_8192_p, rfc3526_8192_g); + + /* + * RFC 4306 + */ + public static readonly DHParameters rfc4306_768 = rfc2409_768; + public static readonly DHParameters rfc4306_1024 = rfc2409_1024; + + /* + * RFC 5114 + */ + private static readonly string rfc5114_1024_160_p = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" + + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" + + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" + + "DF1FB2BC2E4A4371"; + private static readonly string rfc5114_1024_160_g = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" + + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" + + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" + + "855E6EEB22B3B2E5"; + private static readonly string rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353"; + public static readonly DHParameters rfc5114_1024_160 = FromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g, + rfc5114_1024_160_q); + + private static readonly string rfc5114_2048_224_p = "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" + + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212" + + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708" + + "B3BF8A317091883681286130BC8985DB1602E714415D9330" + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D" + + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763" + + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" + "CF9DE5384E71B81C0AC4DFFE0C10E64F"; + private static readonly string rfc5114_2048_224_g = "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF" + + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA" + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7" + + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A" + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE" + + "F180EB34118E98D119529A45D6F834566E3025E316A330EF" + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB" + + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269" + + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA"; + private static readonly string rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB"; + public static readonly DHParameters rfc5114_2048_224 = FromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g, + rfc5114_2048_224_q); + + private static readonly string rfc5114_2048_256_p = "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2" + + "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30" + "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD" + + "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B" + "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C" + + "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E" + "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9" + + "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026" + "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3" + + "75F26375D7014103A4B54330C198AF126116D2276E11715F" + "693877FAD7EF09CADB094AE91E1A1597"; + private static readonly string rfc5114_2048_256_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054" + + "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555" + "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18" + + "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83" + + "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55" + "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14" + + "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6" + + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659"; + private static readonly string rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B" + + "A308B0FE64F5FBD3"; + public static readonly DHParameters rfc5114_2048_256 = FromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g, + rfc5114_2048_256_q); + + /* + * RFC 5996 + */ + public static readonly DHParameters rfc5996_768 = rfc4306_768; + public static readonly DHParameters rfc5996_1024 = rfc4306_1024; + } +} diff --git a/crypto/src/crypto/agreement/srp/SRP6Client.cs b/crypto/src/crypto/agreement/srp/SRP6Client.cs
index 309736564..f075d7ad1 100644 --- a/crypto/src/crypto/agreement/srp/SRP6Client.cs +++ b/crypto/src/crypto/agreement/srp/SRP6Client.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; @@ -24,7 +25,11 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp protected BigInteger u; protected BigInteger S; - protected IDigest digest; + protected BigInteger M1; + protected BigInteger M2; + protected BigInteger Key; + + protected IDigest digest; protected SecureRandom random; public Srp6Client() @@ -46,6 +51,11 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp this.random = random; } + public virtual void Init(Srp6GroupParameters group, IDigest digest, SecureRandom random) + { + Init(group.N, group.G, digest, random); + } + /** * Generates client's credentials given the client's salt, identity and password * @param salt The salt used in the client's verifier. @@ -89,5 +99,66 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp BigInteger tmp = g.ModPow(x, N).Multiply(k).Mod(N); return B.Subtract(tmp).Mod(N).ModPow(exp, N); } + + /** + * Computes the client evidence message M1 using the previously received values. + * To be called after calculating the secret S. + * @return M1: the client side generated evidence message + * @throws CryptoException + */ + public virtual BigInteger CalculateClientEvidenceMessage() + { + // Verify pre-requirements + if (this.pubA == null || this.B == null || this.S == null) + { + throw new CryptoException("Impossible to compute M1: " + + "some data are missing from the previous operations (A,B,S)"); + } + // compute the client evidence message 'M1' + this.M1 = Srp6Utilities.CalculateM1(digest, N, pubA, B, S); + return M1; + } + + /** Authenticates the server evidence message M2 received and saves it only if correct. + * @param M2: the server side generated evidence message + * @return A boolean indicating if the server message M2 was the expected one. + * @throws CryptoException + */ + public virtual bool VerifyServerEvidenceMessage(BigInteger serverM2) + { + // Verify pre-requirements + if (this.pubA == null || this.M1 == null || this.S == null) + { + throw new CryptoException("Impossible to compute and verify M2: " + + "some data are missing from the previous operations (A,M1,S)"); + } + + // Compute the own server evidence message 'M2' + BigInteger computedM2 = Srp6Utilities.CalculateM2(digest, N, pubA, M1, S); + if (computedM2.Equals(serverM2)) + { + this.M2 = serverM2; + return true; + } + return false; + } + + /** + * Computes the final session key as a result of the SRP successful mutual authentication + * To be called after verifying the server evidence message M2. + * @return Key: the mutually authenticated symmetric session key + * @throws CryptoException + */ + public virtual BigInteger CalculateSessionKey() + { + // Verify pre-requirements (here we enforce a previous calculation of M1 and M2) + if (this.S == null || this.M1 == null || this.M2 == null) + { + throw new CryptoException("Impossible to compute Key: " + + "some data are missing from the previous operations (S,M1,M2)"); + } + this.Key = Srp6Utilities.CalculateKey(digest, N, S); + return Key; + } } } diff --git a/crypto/src/crypto/agreement/srp/SRP6Server.cs b/crypto/src/crypto/agreement/srp/SRP6Server.cs
index 35b96d488..fd0c9f1cb 100644 --- a/crypto/src/crypto/agreement/srp/SRP6Server.cs +++ b/crypto/src/crypto/agreement/srp/SRP6Server.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; @@ -26,6 +27,9 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp protected BigInteger u; protected BigInteger S; + protected BigInteger M1; + protected BigInteger M2; + protected BigInteger Key; public Srp6Server() { @@ -49,6 +53,11 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp this.digest = digest; } + public virtual void Init(Srp6GroupParameters group, BigInteger v, IDigest digest, SecureRandom random) + { + Init(group.N, group.G, v, digest, random); + } + /** * Generates the server's credentials that are to be sent to the client. * @return The server's public value to the client @@ -82,9 +91,73 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp return Srp6Utilities.GeneratePrivateValue(digest, N, g, random); } - private BigInteger CalculateS() + private BigInteger CalculateS() { return v.ModPow(u, N).Multiply(A).Mod(N).ModPow(privB, N); } + + /** + * Authenticates the received client evidence message M1 and saves it only if correct. + * To be called after calculating the secret S. + * @param M1: the client side generated evidence message + * @return A boolean indicating if the client message M1 was the expected one. + * @throws CryptoException + */ + public virtual bool VerifyClientEvidenceMessage(BigInteger clientM1) + { + // Verify pre-requirements + if (this.A == null || this.pubB == null || this.S == null) + { + throw new CryptoException("Impossible to compute and verify M1: " + + "some data are missing from the previous operations (A,B,S)"); + } + + // Compute the own client evidence message 'M1' + BigInteger computedM1 = Srp6Utilities.CalculateM1(digest, N, A, pubB, S); + if (computedM1.Equals(clientM1)) + { + this.M1 = clientM1; + return true; + } + return false; + } + + /** + * Computes the server evidence message M2 using the previously verified values. + * To be called after successfully verifying the client evidence message M1. + * @return M2: the server side generated evidence message + * @throws CryptoException + */ + public virtual BigInteger CalculateServerEvidenceMessage() + { + // Verify pre-requirements + if (this.A == null || this.M1 == null || this.S == null) + { + throw new CryptoException("Impossible to compute M2: " + + "some data are missing from the previous operations (A,M1,S)"); + } + + // Compute the server evidence message 'M2' + this.M2 = Srp6Utilities.CalculateM2(digest, N, A, M1, S); + return M2; + } + + /** + * Computes the final session key as a result of the SRP successful mutual authentication + * To be called after calculating the server evidence message M2. + * @return Key: the mutual authenticated symmetric session key + * @throws CryptoException + */ + public virtual BigInteger CalculateSessionKey() + { + // Verify pre-requirements + if (this.S == null || this.M1 == null || this.M2 == null) + { + throw new CryptoException("Impossible to compute Key: " + + "some data are missing from the previous operations (S,M1,M2)"); + } + this.Key = Srp6Utilities.CalculateKey(digest, N, S); + return Key; + } } } diff --git a/crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs b/crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs new file mode 100644
index 000000000..36f4aba11 --- /dev/null +++ b/crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs
@@ -0,0 +1,159 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Agreement.Srp +{ + public class Srp6StandardGroups + { + private static BigInteger FromHex(string hex) + { + return new BigInteger(1, Hex.Decode(hex)); + } + + private static Srp6GroupParameters FromNG(string hexN, string hexG) + { + return new Srp6GroupParameters(FromHex(hexN), FromHex(hexG)); + } + + /* + * RFC 5054 + */ + private const string rfc5054_1024_N = "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C" + + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4" + + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29" + + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3"; + private const string rfc5054_1024_g = "02"; + public static readonly Srp6GroupParameters rfc5054_1024 = FromNG(rfc5054_1024_N, rfc5054_1024_g); + + private const string rfc5054_1536_N = "9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA961" + + "4B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F843" + + "80B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0B" + + "E3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF5" + + "6EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734A" + + "F7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E" + + "8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB"; + private const string rfc5054_1536_g = "02"; + public static readonly Srp6GroupParameters rfc5054_1536 = FromNG(rfc5054_1536_N, rfc5054_1536_g); + + private const string rfc5054_2048_N = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294" + + "3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D" + + "CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB" + + "D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74" + + "7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A" + + "436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D" + + "5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73" + + "03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6" + + "94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F" + "9E4AFF73"; + private const string rfc5054_2048_g = "02"; + public static readonly Srp6GroupParameters rfc5054_2048 = FromNG(rfc5054_2048_N, rfc5054_2048_g); + + private const string rfc5054_3072_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"; + private const string rfc5054_3072_g = "05"; + public static readonly Srp6GroupParameters rfc5054_3072 = FromNG(rfc5054_3072_N, rfc5054_3072_g); + + private const string rfc5054_4096_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF"; + private const string rfc5054_4096_g = "05"; + public static readonly Srp6GroupParameters rfc5054_4096 = FromNG(rfc5054_4096_N, rfc5054_4096_g); + + private const string rfc5054_6144_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF"; + private const string rfc5054_6144_g = "05"; + public static readonly Srp6GroupParameters rfc5054_6144 = FromNG(rfc5054_6144_N, rfc5054_6144_g); + + private const string rfc5054_8192_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + + "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" + + "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" + + "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" + + "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" + + "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" + + "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" + + "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" + + "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"; + private const string rfc5054_8192_g = "13"; + public static readonly Srp6GroupParameters rfc5054_8192 = FromNG(rfc5054_8192_N, rfc5054_8192_g); + } +} diff --git a/crypto/src/crypto/agreement/srp/SRP6Utilities.cs b/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
index 4e790f572..ef6d8f24c 100644 --- a/crypto/src/crypto/agreement/srp/SRP6Utilities.cs +++ b/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
@@ -54,7 +54,75 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp return val; } - private static BigInteger HashPaddedPair(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2) + /** + * Computes the client evidence message (M1) according to the standard routine: + * M1 = H( A | B | S ) + * @param digest The Digest used as the hashing function H + * @param N Modulus used to get the pad length + * @param A The public client value + * @param B The public server value + * @param S The secret calculated by both sides + * @return M1 The calculated client evidence message + */ + public static BigInteger CalculateM1(IDigest digest, BigInteger N, BigInteger A, BigInteger B, BigInteger S) + { + BigInteger M1 = HashPaddedTriplet(digest, N, A, B, S); + return M1; + } + + /** + * Computes the server evidence message (M2) according to the standard routine: + * M2 = H( A | M1 | S ) + * @param digest The Digest used as the hashing function H + * @param N Modulus used to get the pad length + * @param A The public client value + * @param M1 The client evidence message + * @param S The secret calculated by both sides + * @return M2 The calculated server evidence message + */ + public static BigInteger CalculateM2(IDigest digest, BigInteger N, BigInteger A, BigInteger M1, BigInteger S) + { + BigInteger M2 = HashPaddedTriplet(digest, N, A, M1, S); + return M2; + } + + /** + * Computes the final Key according to the standard routine: Key = H(S) + * @param digest The Digest used as the hashing function H + * @param N Modulus used to get the pad length + * @param S The secret calculated by both sides + * @return + */ + public static BigInteger CalculateKey(IDigest digest, BigInteger N, BigInteger S) + { + int padLength = (N.BitLength + 7) / 8; + byte[] _S = GetPadded(S, padLength); + digest.BlockUpdate(_S, 0, _S.Length); + + byte[] output = new byte[digest.GetDigestSize()]; + digest.DoFinal(output, 0); + return new BigInteger(1, output); + } + + private static BigInteger HashPaddedTriplet(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2, BigInteger n3) + { + int padLength = (N.BitLength + 7) / 8; + + byte[] n1_bytes = GetPadded(n1, padLength); + byte[] n2_bytes = GetPadded(n2, padLength); + byte[] n3_bytes = GetPadded(n3, padLength); + + digest.BlockUpdate(n1_bytes, 0, n1_bytes.Length); + digest.BlockUpdate(n2_bytes, 0, n2_bytes.Length); + digest.BlockUpdate(n3_bytes, 0, n3_bytes.Length); + + byte[] output = new byte[digest.GetDigestSize()]; + digest.DoFinal(output, 0); + + return new BigInteger(1, output); + } + + private static BigInteger HashPaddedPair(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2) { int padLength = (N.BitLength + 7) / 8; diff --git a/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs b/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
index 264833b4d..956973598 100644 --- a/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs +++ b/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Crypto.Agreement.Srp @@ -31,7 +32,12 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp this.digest = digest; } - /** + public virtual void Init(Srp6GroupParameters group, IDigest digest) + { + Init(group.N, group.G, digest); + } + + /** * Creates a new SRP verifier * @param salt The salt to use, generally should be large and random * @param identity The user's identifying information (eg. username) diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs new file mode 100644
index 000000000..2d6cf393c --- /dev/null +++ b/crypto/src/crypto/digests/KeccakDigest.cs
@@ -0,0 +1,534 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// <summary> + /// Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + /// </summary> + /// <remarks> + /// Following the naming conventions used in the C source code to enable easy review of the implementation. + /// </remarks> + public class KeccakDigest + : IDigest, IMemoable + { + private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants(); + + private static readonly int[] KeccakRhoOffsets = KeccakInitializeRhoOffsets(); + + private static ulong[] KeccakInitializeRoundConstants() + { + ulong[] keccakRoundConstants = new ulong[24]; + byte LFSRState = 0x01; + + for (int i = 0; i < 24; i++) + { + keccakRoundConstants[i] = 0; + for (int j = 0; j < 7; j++) + { + int bitPosition = (1 << j) - 1; + + // LFSR86540 + + bool loBit = (LFSRState & 0x01) != 0; + if (loBit) + { + keccakRoundConstants[i] ^= 1UL << bitPosition; + } + + bool hiBit = (LFSRState & 0x80) != 0; + LFSRState <<= 1; + if (hiBit) + { + LFSRState ^= 0x71; + } + + } + } + + return keccakRoundConstants; + } + + private static int[] KeccakInitializeRhoOffsets() + { + int[] keccakRhoOffsets = new int[25]; + int x, y, t, newX, newY; + + int rhoOffset = 0; + keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = rhoOffset; + x = 1; + y = 0; + for (t = 1; t < 25; t++) + { + //rhoOffset = ((t + 1) * (t + 2) / 2) % 64; + rhoOffset = (rhoOffset + t) & 63; + keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset; + newX = (0 * x + 1 * y) % 5; + newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + return keccakRhoOffsets; + } + + protected byte[] state = new byte[(1600 / 8)]; + protected byte[] dataQueue = new byte[(1536 / 8)]; + protected int rate; + protected int bitsInQueue; + protected int fixedOutputLength; + protected bool squeezing; + protected int bitsAvailableForSqueezing; + protected byte[] chunk; + protected byte[] oneByte; + + private void ClearDataQueueSection(int off, int len) + { + for (int i = off; i != off + len; i++) + { + dataQueue[i] = 0; + } + } + + public KeccakDigest() + : this(288) + { + } + + public KeccakDigest(int bitLength) + { + Init(bitLength); + } + + public KeccakDigest(KeccakDigest source) + { + CopyIn(source); + } + + private void CopyIn(KeccakDigest source) + { + Array.Copy(source.state, 0, this.state, 0, source.state.Length); + Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length); + this.rate = source.rate; + this.bitsInQueue = source.bitsInQueue; + this.fixedOutputLength = source.fixedOutputLength; + this.squeezing = source.squeezing; + this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing; + this.chunk = Arrays.Clone(source.chunk); + this.oneByte = Arrays.Clone(source.oneByte); + } + + public virtual string AlgorithmName + { + get { return "Keccak-" + fixedOutputLength; } + } + + public virtual int GetDigestSize() + { + return fixedOutputLength / 8; + } + + public virtual void Update(byte input) + { + oneByte[0] = input; + + Absorb(oneByte, 0, 8L); + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + Absorb(input, inOff, len * 8L); + } + + public virtual int DoFinal(byte[] output, int outOff) + { + Squeeze(output, outOff, fixedOutputLength); + + Reset(); + + return GetDigestSize(); + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits) + { + if (partialBits > 0) + { + oneByte[0] = partialByte; + Absorb(oneByte, 0, partialBits); + } + + Squeeze(output, outOff, fixedOutputLength); + + Reset(); + + return GetDigestSize(); + } + + public virtual void Reset() + { + Init(fixedOutputLength); + } + + /** + * Return the size of block that the compression function is applied to in bytes. + * + * @return internal byte length of a block. + */ + public virtual int GetByteLength() + { + return rate / 8; + } + + private void Init(int bitLength) + { + switch (bitLength) + { + case 128: + InitSponge(1344, 256); + break; + case 224: + InitSponge(1152, 448); + break; + case 256: + InitSponge(1088, 512); + break; + case 288: + InitSponge(1024, 576); + break; + case 384: + InitSponge(832, 768); + break; + case 512: + InitSponge(576, 1024); + break; + default: + throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength"); + } + } + + private void InitSponge(int rate, int capacity) + { + if (rate + capacity != 1600) + { + throw new InvalidOperationException("rate + capacity != 1600"); + } + if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) + { + throw new InvalidOperationException("invalid rate value"); + } + + this.rate = rate; + // this is never read, need to check to see why we want to save it + // this.capacity = capacity; + this.fixedOutputLength = 0; + Arrays.Fill(this.state, (byte)0); + Arrays.Fill(this.dataQueue, (byte)0); + this.bitsInQueue = 0; + this.squeezing = false; + this.bitsAvailableForSqueezing = 0; + this.fixedOutputLength = capacity / 2; + this.chunk = new byte[rate / 8]; + this.oneByte = new byte[1]; + } + + private void AbsorbQueue() + { + KeccakAbsorb(state, dataQueue, rate / 8); + + bitsInQueue = 0; + } + + protected virtual void Absorb(byte[] data, int off, long databitlen) + { + long i, j, wholeBlocks; + + if ((bitsInQueue % 8) != 0) + { + throw new InvalidOperationException("attempt to absorb with odd length queue."); + } + if (squeezing) + { + throw new InvalidOperationException("attempt to absorb while squeezing."); + } + + i = 0; + while (i < databitlen) + { + if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate))) + { + wholeBlocks = (databitlen - i) / rate; + + for (j = 0; j < wholeBlocks; j++) + { + Array.Copy(data, (int)(off + (i / 8) + (j * chunk.Length)), chunk, 0, chunk.Length); + + KeccakAbsorb(state, chunk, chunk.Length); + } + + i += wholeBlocks * rate; + } + else + { + int partialBlock = (int)(databitlen - i); + if (partialBlock + bitsInQueue > rate) + { + partialBlock = rate - bitsInQueue; + } + int partialByte = partialBlock % 8; + partialBlock -= partialByte; + Array.Copy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8); + + bitsInQueue += partialBlock; + i += partialBlock; + if (bitsInQueue == rate) + { + AbsorbQueue(); + } + if (partialByte > 0) + { + int mask = (1 << partialByte) - 1; + dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask); + bitsInQueue += partialByte; + i += partialByte; + } + } + } + } + + private void PadAndSwitchToSqueezingPhase() + { + if (bitsInQueue + 1 == rate) + { + dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); + AbsorbQueue(); + ClearDataQueueSection(0, rate / 8); + } + else + { + ClearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); + dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); + } + dataQueue[(rate - 1) / 8] |= (byte)(1U << ((rate - 1) % 8)); + AbsorbQueue(); + + if (rate == 1024) + { + KeccakExtract1024bits(state, dataQueue); + bitsAvailableForSqueezing = 1024; + } + else + { + KeccakExtract(state, dataQueue, rate / 64); + bitsAvailableForSqueezing = rate; + } + + squeezing = true; + } + + protected virtual void Squeeze(byte[] output, int offset, long outputLength) + { + long i; + int partialBlock; + + if (!squeezing) + { + PadAndSwitchToSqueezingPhase(); + } + if ((outputLength % 8) != 0) + { + throw new InvalidOperationException("outputLength not a multiple of 8"); + } + + i = 0; + while (i < outputLength) + { + if (bitsAvailableForSqueezing == 0) + { + KeccakPermutation(state); + + if (rate == 1024) + { + KeccakExtract1024bits(state, dataQueue); + bitsAvailableForSqueezing = 1024; + } + else + { + KeccakExtract(state, dataQueue, rate / 64); + bitsAvailableForSqueezing = rate; + } + } + partialBlock = bitsAvailableForSqueezing; + if ((long)partialBlock > outputLength - i) + { + partialBlock = (int)(outputLength - i); + } + + Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8); + bitsAvailableForSqueezing -= partialBlock; + i += partialBlock; + } + } + + private static void FromBytesToWords(ulong[] stateAsWords, byte[] state) + { + for (int i = 0; i < (1600 / 64); i++) + { + stateAsWords[i] = 0; + int index = i * (64 / 8); + for (int j = 0; j < (64 / 8); j++) + { + stateAsWords[i] |= ((ulong)state[index + j] & 0xff) << ((8 * j)); + } + } + } + + private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords) + { + for (int i = 0; i < (1600 / 64); i++) + { + int index = i * (64 / 8); + for (int j = 0; j < (64 / 8); j++) + { + state[index + j] = (byte)(stateAsWords[i] >> (8 * j)); + } + } + } + + private void KeccakPermutation(byte[] state) + { + ulong[] longState = new ulong[state.Length / 8]; + + FromBytesToWords(longState, state); + + KeccakPermutationOnWords(longState); + + FromWordsToBytes(state, longState); + } + + private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) + { + for (int i = 0; i < dataLengthInBytes; i++) + { + state[i] ^= data[i]; + } + + KeccakPermutation(state); + } + + private void KeccakPermutationOnWords(ulong[] state) + { + int i; + + for (i = 0; i < 24; i++) + { + Theta(state); + Rho(state); + Pi(state); + Chi(state); + Iota(state, i); + } + } + + ulong[] C = new ulong[5]; + + private void Theta(ulong[] A) + { + for (int x = 0; x < 5; x++) + { + C[x] = 0; + for (int y = 0; y < 5; y++) + { + C[x] ^= A[x + 5 * y]; + } + } + for (int x = 0; x < 5; x++) + { + ulong dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >> (64 - 1)))) ^ C[(x + 4) % 5]; + for (int y = 0; y < 5; y++) + { + A[x + 5 * y] ^= dX; + } + } + } + + private void Rho(ulong[] A) + { + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + int index = x + 5 * y; + A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >> (64 - KeccakRhoOffsets[index]))) : A[index]); + } + } + } + + ulong[] tempA = new ulong[25]; + + private void Pi(ulong[] A) + { + Array.Copy(A, 0, tempA, 0, tempA.Length); + + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; + } + } + } + + ulong[] chiC = new ulong[5]; + + private void Chi(ulong[] A) + { + for (int y = 0; y < 5; y++) + { + for (int x = 0; x < 5; x++) + { + chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); + } + for (int x = 0; x < 5; x++) + { + A[x + 5 * y] = chiC[x]; + } + } + } + + private static void Iota(ulong[] A, int indexRound) + { + A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; + } + + private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) + { + KeccakPermutationAfterXor(byteState, data, dataInBytes); + } + + private void KeccakExtract1024bits(byte[] byteState, byte[] data) + { + Array.Copy(byteState, 0, data, 0, 128); + } + + private void KeccakExtract(byte[] byteState, byte[] data, int laneCount) + { + Array.Copy(byteState, 0, data, 0, laneCount * 8); + } + + public virtual IMemoable Copy() + { + return new KeccakDigest(this); + } + + public virtual void Reset(IMemoable other) + { + KeccakDigest d = (KeccakDigest)other; + + CopyIn(d); + } + } +} diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs
index 2c6837b3c..890b665cf 100644 --- a/crypto/src/crypto/digests/SHA3Digest.cs +++ b/crypto/src/crypto/digests/SHA3Digest.cs
@@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Org.BouncyCastle.Utilities; @@ -11,550 +12,75 @@ namespace Org.BouncyCastle.Crypto.Digests /// Following the naming conventions used in the C source code to enable easy review of the implementation. /// </remarks> public class Sha3Digest - : IDigest, IMemoable + : KeccakDigest { - private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants(); - - private static readonly int[] KeccakRhoOffsets = KeccakInitializeRhoOffsets(); - - private static ulong[] KeccakInitializeRoundConstants() - { - ulong[] keccakRoundConstants = new ulong[24]; - byte LFSRState = 0x01; - - for (int i = 0; i < 24; i++) - { - keccakRoundConstants[i] = 0; - for (int j = 0; j < 7; j++) - { - int bitPosition = (1 << j) - 1; - - // LFSR86540 - - bool loBit = (LFSRState & 0x01) != 0; - if (loBit) - { - keccakRoundConstants[i] ^= 1UL << bitPosition; - } - - bool hiBit = (LFSRState & 0x80) != 0; - LFSRState <<= 1; - if (hiBit) - { - LFSRState ^= 0x71; - } - - } - } - - return keccakRoundConstants; - } - - private static int[] KeccakInitializeRhoOffsets() - { - int[] keccakRhoOffsets = new int[25]; - int x, y, t, newX, newY; - - int rhoOffset = 0; - keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = rhoOffset; - x = 1; - y = 0; - for (t = 1; t < 25; t++) - { - //rhoOffset = ((t + 1) * (t + 2) / 2) % 64; - rhoOffset = (rhoOffset + t) & 63; - keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset; - newX = (0 * x + 1 * y) % 5; - newY = (2 * x + 3 * y) % 5; - x = newX; - y = newY; - } - - return keccakRhoOffsets; - } - - private byte[] state = new byte[(1600 / 8)]; - private byte[] dataQueue = new byte[(1536 / 8)]; - private int rate; - private int bitsInQueue; - private int fixedOutputLength; - private bool squeezing; - private int bitsAvailableForSqueezing; - private byte[] chunk; - private byte[] oneByte; - - private void ClearDataQueueSection(int off, int len) - { - for (int i = off; i != off + len; i++) - { - dataQueue[i] = 0; - } - } - - public Sha3Digest() - { - Init(0); - } - - public Sha3Digest(int bitLength) - { - Init(bitLength); - } - - public Sha3Digest(Sha3Digest source) - { - CopyIn(source); - } - - private void CopyIn(Sha3Digest source) - { - Array.Copy(source.state, 0, this.state, 0, source.state.Length); - Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length); - this.rate = source.rate; - this.bitsInQueue = source.bitsInQueue; - this.fixedOutputLength = source.fixedOutputLength; - this.squeezing = source.squeezing; - this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing; - this.chunk = Arrays.Clone(source.chunk); - this.oneByte = Arrays.Clone(source.oneByte); - } - - public virtual string AlgorithmName - { - get { return "SHA3-" + fixedOutputLength; } - } - - public virtual int GetDigestSize() - { - return fixedOutputLength / 8; - } - - public virtual void Update(byte input) - { - oneByte[0] = input; - - DoUpdate(oneByte, 0, 8L); - } - - public virtual void BlockUpdate(byte[] input, int inOff, int len) - { - DoUpdate(input, inOff, len * 8L); - } - - public virtual int DoFinal(byte[] output, int outOff) - { - Squeeze(output, outOff, fixedOutputLength); - - Reset(); - - return GetDigestSize(); - } - - public virtual void Reset() - { - Init(fixedOutputLength); - } - - /** - * Return the size of block that the compression function is applied to in bytes. - * - * @return internal byte length of a block. - */ - public virtual int GetByteLength() - { - return rate / 8; - } - - private void Init(int bitLength) + private static int CheckBitLength(int bitLength) { switch (bitLength) { - case 0: - case 288: - InitSponge(1024, 576); - break; case 224: - InitSponge(1152, 448); - break; case 256: - InitSponge(1088, 512); - break; case 384: - InitSponge(832, 768); - break; case 512: - InitSponge(576, 1024); - break; + return bitLength; default: - throw new ArgumentException("must be one of 224, 256, 384, or 512.", "bitLength"); - } - } - - private void DoUpdate(byte[] data, int off, long databitlen) - { - if ((databitlen % 8) == 0) - { - Absorb(data, off, databitlen); - } - else - { - Absorb(data, off, databitlen - (databitlen % 8)); - - byte[] lastByte = new byte[1]; - - lastByte[0] = (byte)(data[off + (int)(databitlen / 8)] >> (int)(8 - (databitlen % 8))); - Absorb(lastByte, off, databitlen % 8); - } - } - - private void InitSponge(int rate, int capacity) - { - if (rate + capacity != 1600) - { - throw new InvalidOperationException("rate + capacity != 1600"); - } - if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) - { - throw new InvalidOperationException("invalid rate value"); - } - - this.rate = rate; - // this is never read, need to check to see why we want to save it - // this.capacity = capacity; - this.fixedOutputLength = 0; - Arrays.Fill(this.state, (byte)0); - Arrays.Fill(this.dataQueue, (byte)0); - this.bitsInQueue = 0; - this.squeezing = false; - this.bitsAvailableForSqueezing = 0; - this.fixedOutputLength = capacity / 2; - this.chunk = new byte[rate / 8]; - this.oneByte = new byte[1]; - } - - private void AbsorbQueue() - { - KeccakAbsorb(state, dataQueue, rate / 8); - - bitsInQueue = 0; - } - - private void Absorb(byte[] data, int off, long databitlen) - { - long i, j, wholeBlocks; - - if ((bitsInQueue % 8) != 0) - { - throw new InvalidOperationException("attempt to absorb with odd length queue."); - } - if (squeezing) - { - throw new InvalidOperationException("attempt to absorb while squeezing."); - } - - i = 0; - while (i < databitlen) - { - if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate))) - { - wholeBlocks = (databitlen - i) / rate; - - for (j = 0; j < wholeBlocks; j++) - { - Array.Copy(data, (int)(off + (i / 8) + (j * chunk.Length)), chunk, 0, chunk.Length); - - //displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8); - - KeccakAbsorb(state, chunk, chunk.Length); - } - - i += wholeBlocks * rate; - } - else - { - int partialBlock = (int)(databitlen - i); - if (partialBlock + bitsInQueue > rate) - { - partialBlock = rate - bitsInQueue; - } - int partialByte = partialBlock % 8; - partialBlock -= partialByte; - Array.Copy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8); - - bitsInQueue += partialBlock; - i += partialBlock; - if (bitsInQueue == rate) - { - AbsorbQueue(); - } - if (partialByte > 0) - { - int mask = (1 << partialByte) - 1; - dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask); - bitsInQueue += partialByte; - i += partialByte; - } - } + throw new ArgumentException(bitLength + " not supported for SHA-3", "bitLength"); } } - private void PadAndSwitchToSqueezingPhase() - { - if (bitsInQueue + 1 == rate) - { - dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); - AbsorbQueue(); - ClearDataQueueSection(0, rate / 8); - } - else - { - ClearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); - dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); - } - dataQueue[(rate - 1) / 8] |= (byte)(1U << ((rate - 1) % 8)); - AbsorbQueue(); - - //displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---"); - - if (rate == 1024) - { - KeccakExtract1024bits(state, dataQueue); - bitsAvailableForSqueezing = 1024; - } - else - { - KeccakExtract(state, dataQueue, rate / 64); - bitsAvailableForSqueezing = rate; - } - - //displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); - - squeezing = true; - } - - private void Squeeze(byte[] output, int offset, long outputLength) - { - long i; - int partialBlock; - - if (!squeezing) - { - PadAndSwitchToSqueezingPhase(); - } - if ((outputLength % 8) != 0) - { - throw new InvalidOperationException("outputLength not a multiple of 8"); - } - - i = 0; - while (i < outputLength) - { - if (bitsAvailableForSqueezing == 0) - { - KeccakPermutation(state); - - if (rate == 1024) - { - KeccakExtract1024bits(state, dataQueue); - bitsAvailableForSqueezing = 1024; - } - else - - { - KeccakExtract(state, dataQueue, rate / 64); - bitsAvailableForSqueezing = rate; - } - - //displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); - - } - partialBlock = bitsAvailableForSqueezing; - if ((long)partialBlock > outputLength - i) - { - partialBlock = (int)(outputLength - i); - } - - Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8); - bitsAvailableForSqueezing -= partialBlock; - i += partialBlock; - } - } - - private static void FromBytesToWords(ulong[] stateAsWords, byte[] state) - { - for (int i = 0; i < (1600 / 64); i++) - { - stateAsWords[i] = 0; - int index = i * (64 / 8); - for (int j = 0; j < (64 / 8); j++) - { - stateAsWords[i] |= ((ulong)state[index + j] & 0xff) << ((8 * j)); - } - } - } - - private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords) + public Sha3Digest() + : this(256) { - for (int i = 0; i < (1600 / 64); i++) - { - int index = i * (64 / 8); - for (int j = 0; j < (64 / 8); j++) - { - state[index + j] = (byte)(stateAsWords[i] >> (8 * j)); - } - } } - private void KeccakPermutation(byte[] state) + public Sha3Digest(int bitLength) + : base(CheckBitLength(bitLength)) { - ulong[] longState = new ulong[state.Length / 8]; - - FromBytesToWords(longState, state); - - //displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState); - - KeccakPermutationOnWords(longState); - - //displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState); - - FromWordsToBytes(state, longState); } - private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) + public Sha3Digest(Sha3Digest source) + : base(source) { - for (int i = 0; i < dataLengthInBytes; i++) - { - state[i] ^= data[i]; - } - - KeccakPermutation(state); } - private void KeccakPermutationOnWords(ulong[] state) + public override string AlgorithmName { - int i; - - //displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state); - - for (i = 0; i < 24; i++) - { - //displayIntermediateValues.displayRoundNumber(3, i); - - Theta(state); - //displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state); - - Rho(state); - //displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state); - - Pi(state); - //displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state); - - Chi(state); - //displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state); - - Iota(state, i); - //displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state); - } + get { return "SHA3-" + fixedOutputLength; } } - ulong[] C = new ulong[5]; - - private void Theta(ulong[] A) + public override int DoFinal(byte[] output, int outOff) { - for (int x = 0; x < 5; x++) - { - C[x] = 0; - for (int y = 0; y < 5; y++) - { - C[x] ^= A[x + 5 * y]; - } - } - for (int x = 0; x < 5; x++) - { - ulong dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >> (64 - 1)))) ^ C[(x + 4) % 5]; - for (int y = 0; y < 5; y++) - { - A[x + 5 * y] ^= dX; - } - } - } + Absorb(new byte[]{ 0x02 }, 0, 2); - private void Rho(ulong[] A) - { - for (int x = 0; x < 5; x++) - { - for (int y = 0; y < 5; y++) - { - int index = x + 5 * y; - A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >> (64 - KeccakRhoOffsets[index]))) : A[index]); - } - } + return base.DoFinal(output, outOff); } - ulong[] tempA = new ulong[25]; - - private void Pi(ulong[] A) + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected override int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits) { - Array.Copy(A, 0, tempA, 0, tempA.Length); - - for (int x = 0; x < 5; x++) - { - for (int y = 0; y < 5; y++) - { - A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; - } - } - } + if (partialBits < 0 || partialBits > 7) + throw new ArgumentException("must be in the range [0,7]", "partialBits"); - ulong[] chiC = new ulong[5]; + int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x02 << partialBits); + Debug.Assert(finalInput >= 0); + int finalBits = partialBits + 2; - private void Chi(ulong[] A) - { - for (int y = 0; y < 5; y++) + if (finalBits >= 8) { - for (int x = 0; x < 5; x++) - { - chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); - } - for (int x = 0; x < 5; x++) - { - A[x + 5 * y] = chiC[x]; - } + oneByte[0] = (byte)finalInput; + Absorb(oneByte, 0, 8); + finalBits -= 8; + finalInput >>= 8; } - } - - private static void Iota(ulong[] A, int indexRound) - { - A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; - } - - private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) - { - KeccakPermutationAfterXor(byteState, data, dataInBytes); - } - - private void KeccakExtract1024bits(byte[] byteState, byte[] data) - { - Array.Copy(byteState, 0, data, 0, 128); - } - private void KeccakExtract(byte[] byteState, byte[] data, int laneCount) - { - Array.Copy(byteState, 0, data, 0, laneCount * 8); + return base.DoFinal(output, outOff, (byte)finalInput, finalBits); } - public IMemoable Copy() + public override IMemoable Copy() { return new Sha3Digest(this); } - - public void Reset(IMemoable other) - { - Sha3Digest d = (Sha3Digest)other; - - CopyIn(d); - } - - } } diff --git a/crypto/src/crypto/digests/ShakeDigest.cs b/crypto/src/crypto/digests/ShakeDigest.cs new file mode 100644
index 000000000..fd7d85681 --- /dev/null +++ b/crypto/src/crypto/digests/ShakeDigest.cs
@@ -0,0 +1,111 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// <summary> + /// Implementation of SHAKE based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + /// </summary> + /// <remarks> + /// Following the naming conventions used in the C source code to enable easy review of the implementation. + /// </remarks> + public class ShakeDigest + : KeccakDigest, IXof + { + private static int CheckBitLength(int bitLength) + { + switch (bitLength) + { + case 128: + case 256: + return bitLength; + default: + throw new ArgumentException(bitLength + " not supported for SHAKE", "bitLength"); + } + } + + public ShakeDigest() + : this(128) + { + } + + public ShakeDigest(int bitLength) + : base(CheckBitLength(bitLength)) + { + } + + public ShakeDigest(ShakeDigest source) + : base(source) + { + } + + public override string AlgorithmName + { + get { return "SHAKE" + fixedOutputLength; } + } + + public override int DoFinal(byte[] output, int outOff) + { + return DoFinal(output, outOff, GetDigestSize()); + } + + public virtual int DoFinal(byte[] output, int outOff, int outLen) + { + Absorb(new byte[]{ 0x0F }, 0, 4); + + Squeeze(output, outOff, ((long)outLen) * 8); + + Reset(); + + return outLen; + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected override int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits) + { + return DoFinal(output, outOff, GetDigestSize(), partialByte, partialBits); + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected virtual int DoFinal(byte[] output, int outOff, int outLen, byte partialByte, int partialBits) + { + if (partialBits < 0 || partialBits > 7) + throw new ArgumentException("must be in the range [0,7]", "partialBits"); + + int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits); + Debug.Assert(finalInput >= 0); + int finalBits = partialBits + 4; + + if (finalBits >= 8) + { + oneByte[0] = (byte)finalInput; + Absorb(oneByte, 0, 8); + finalBits -= 8; + finalInput >>= 8; + } + + if (finalBits > 0) + { + oneByte[0] = (byte)finalInput; + Absorb(oneByte, 0, finalBits); + } + + Squeeze(output, outOff, ((long)outLen) * 8); + + Reset(); + + return outLen; + } + + public override IMemoable Copy() + { + return new ShakeDigest(this); + } + } +} diff --git a/crypto/src/crypto/ec/CustomNamedCurves.cs b/crypto/src/crypto/ec/CustomNamedCurves.cs
index 5d2369349..51bb1829a 100644 --- a/crypto/src/crypto/ec/CustomNamedCurves.cs +++ b/crypto/src/crypto/ec/CustomNamedCurves.cs
@@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using Org.BouncyCastle.Asn1; @@ -60,7 +60,7 @@ namespace Org.BouncyCastle.Crypto.EC * * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14) */ - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A" + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9")); @@ -69,14 +69,110 @@ namespace Org.BouncyCastle.Crypto.EC } /* + * secp128r1 + */ + internal class SecP128R1Holder + : X9ECParametersHolder + { + private SecP128R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecP128R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("000E0D4D696E6768756151750CC03A4473D03679"); + ECCurve curve = ConfigureCurve(new SecP128R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "161FF7528B899B2D0C28607CA52C5B86" + + "CF5AC8395BAFEB13C02DA292DDED7A83")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * secp160k1 + */ + internal class SecP160K1Holder + : X9ECParametersHolder + { + private SecP160K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecP160K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("9ba48cba5ebcb9b6bd33b92830b2a2e0e192f10a", 16), + new BigInteger("c39c6c3b3a36d7701b9c71a1f5804ae5d0003f4", 16), + new BigInteger[]{ + new BigInteger("9162fbe73984472a0a9e", 16), + new BigInteger("-96341f1138933bc2f505", 16) }, + new BigInteger[]{ + new BigInteger("127971af8721782ecffa3", 16), + new BigInteger("9162fbe73984472a0a9e", 16) }, + new BigInteger("9162fbe73984472a0a9d0590", 16), + new BigInteger("96341f1138933bc2f503fd44", 16), + 176); + ECCurve curve = ConfigureCurveGlv(new SecP160K1Curve(), glv); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + + "938CF935318FDCED6BC28286531733C3F03C4FEE")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * secp160r1 + */ + internal class SecP160R1Holder + : X9ECParametersHolder + { + private SecP160R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecP160R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("1053CDE42C14D696E67687561517533BF3F83345"); + ECCurve curve = ConfigureCurve(new SecP160R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "4A96B5688EF573284664698968C38BB913CBFC82" + + "23A628553168947D59DCC912042351377AC5FB32")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * secp160r2 + */ + internal class SecP160R2Holder + : X9ECParametersHolder + { + private SecP160R2Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecP160R2Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("B99B99B099B323E02709A4D696E6768756151751"); + ECCurve curve = ConfigureCurve(new SecP160R2Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "52DCB034293A117E1F4FF11B30F7199D3144CE6D" + + "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* * secp192k1 */ - internal class Secp192k1Holder + internal class SecP192K1Holder : X9ECParametersHolder { - private Secp192k1Holder() { } + private SecP192K1Holder() { } - internal static readonly X9ECParametersHolder Instance = new Secp192k1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP192K1Holder(); protected override X9ECParameters CreateParameters() { @@ -90,11 +186,11 @@ namespace Org.BouncyCastle.Crypto.EC new BigInteger[]{ new BigInteger("12511cfe811d0f4e6bc688b4d", 16), new BigInteger("71169be7330b3038edb025f1", 16) }, - new BigInteger("1c45a6f9ccc2cc0e3b6c097c7", 16), - new BigInteger("2cfecd0037b1712b73ae19575", 16), - 194); + new BigInteger("71169be7330b3038edb025f1d0f9", 16), + new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16), + 208); ECCurve curve = ConfigureCurveGlv(new SecP192K1Curve(), glv); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -104,18 +200,18 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp192r1 */ - internal class Secp192r1Holder + internal class SecP192R1Holder : X9ECParametersHolder { - private Secp192r1Holder() { } + private SecP192R1Holder() { } - internal static readonly X9ECParametersHolder Instance = new Secp192r1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP192R1Holder(); protected override X9ECParameters CreateParameters() { byte[] S = Hex.Decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); ECCurve curve = ConfigureCurve(new SecP192R1Curve()); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -125,12 +221,12 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp224k1 */ - internal class Secp224k1Holder + internal class SecP224K1Holder : X9ECParametersHolder { - private Secp224k1Holder() { } + private SecP224K1Holder() { } - internal static readonly X9ECParametersHolder Instance = new Secp224k1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP224K1Holder(); protected override X9ECParameters CreateParameters() { @@ -144,11 +240,11 @@ namespace Org.BouncyCastle.Crypto.EC new BigInteger[]{ new BigInteger("1243ae1b4d71613bc9f780a03690e", 16), new BigInteger("6b8cf07d4ca75c88957d9d670591", 16) }, - new BigInteger("35c6783ea653ae444abeceb382c82", 16), - new BigInteger("5c56f89bc5375b9a04fd364e31bdd", 16), - 227); + new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16), + new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16), + 240); ECCurve curve = ConfigureCurveGlv(new SecP224K1Curve(), glv); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C" + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -158,18 +254,18 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp224r1 */ - internal class Secp224r1Holder + internal class SecP224R1Holder : X9ECParametersHolder { - private Secp224r1Holder() { } + private SecP224R1Holder() { } - internal static readonly X9ECParametersHolder Instance = new Secp224r1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP224R1Holder(); protected override X9ECParameters CreateParameters() { byte[] S = Hex.Decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); ECCurve curve = ConfigureCurve(new SecP224R1Curve()); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -179,12 +275,12 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp256k1 */ - internal class Secp256k1Holder + internal class SecP256K1Holder : X9ECParametersHolder { - private Secp256k1Holder() {} + private SecP256K1Holder() {} - internal static readonly X9ECParametersHolder Instance = new Secp256k1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP256K1Holder(); protected override X9ECParameters CreateParameters() { @@ -198,11 +294,11 @@ namespace Org.BouncyCastle.Crypto.EC new BigInteger[]{ new BigInteger("114ca50f7a8e2f3f657c1108d9d44cfd8", 16), new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16) }, - new BigInteger("c21b48869f51af37a1b243924a13ac55", 16), - new BigInteger("3910dfb58043a20a1bd51fea42aff9311", 16), - 258); + new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16), + new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16), + 272); ECCurve curve = ConfigureCurveGlv(new SecP256K1Curve(), glv); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -212,18 +308,18 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp256r1 */ - internal class Secp256r1Holder + internal class SecP256R1Holder : X9ECParametersHolder { - private Secp256r1Holder() {} + private SecP256R1Holder() {} - internal static readonly X9ECParametersHolder Instance = new Secp256r1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP256R1Holder(); protected override X9ECParameters CreateParameters() { byte[] S = Hex.Decode("C49D360886E704936A6678E1139D26B7819F7E90"); ECCurve curve = ConfigureCurve(new SecP256R1Curve()); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -233,18 +329,18 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp384r1 */ - internal class Secp384r1Holder + internal class SecP384R1Holder : X9ECParametersHolder { - private Secp384r1Holder() { } + private SecP384R1Holder() { } - internal static readonly X9ECParametersHolder Instance = new Secp384r1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP384R1Holder(); protected override X9ECParameters CreateParameters() { byte[] S = Hex.Decode("A335926AA319A27A1D00896A6773A4827ACDAC73"); ECCurve curve = ConfigureCurve(new SecP384R1Curve()); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7" + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); @@ -254,61 +350,487 @@ namespace Org.BouncyCastle.Crypto.EC /* * secp521r1 */ - internal class Secp521r1Holder + internal class SecP521R1Holder : X9ECParametersHolder { - private Secp521r1Holder() { } + private SecP521R1Holder() { } - internal static readonly X9ECParametersHolder Instance = new Secp521r1Holder(); + internal static readonly X9ECParametersHolder Instance = new SecP521R1Holder(); protected override X9ECParameters CreateParameters() { byte[] S = Hex.Decode("D09E8800291CB85396CC6717393284AAA0DA64BA"); ECCurve curve = ConfigureCurve(new SecP521R1Curve()); - ECPoint G = curve.DecodePoint(Hex.Decode("04" + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66" + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650")); return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); } } + /* + * sect113r1 + */ + internal class SecT113R1Holder + : X9ECParametersHolder + { + private SecT113R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT113R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("10E723AB14D696E6768756151756FEBF8FCB49A9"); + ECCurve curve = ConfigureCurve(new SecT113R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "009D73616F35F4AB1407D73562C10F" + + "00A52830277958EE84D1315ED31886")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect113r2 + */ + internal class SecT113R2Holder + : X9ECParametersHolder + { + private SecT113R2Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT113R2Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("10C0FB15760860DEF1EEF4D696E676875615175D"); + ECCurve curve = ConfigureCurve(new SecT113R2Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "01A57A6A7B26CA5EF52FCDB8164797" + + "00B3ADC94ED1FE674C06E695BABA1D")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect131r1 + */ + internal class SecT131R1Holder + : X9ECParametersHolder + { + private SecT131R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT131R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("4D696E676875615175985BD3ADBADA21B43A97E2"); + ECCurve curve = ConfigureCurve(new SecT131R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "0081BAF91FDF9833C40F9C181343638399" + + "078C6E7EA38C001F73C8134B1B4EF9E150")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect131r2 + */ + internal class SecT131R2Holder + : X9ECParametersHolder + { + private SecT131R2Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT131R2Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("985BD3ADBAD4D696E676875615175A21B43A97E3"); + ECCurve curve = ConfigureCurve(new SecT131R2Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "0356DCD8F2F95031AD652D23951BB366A8" + + "0648F06D867940A5366D9E265DE9EB240F")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect163k1 + */ + internal class SecT163K1Holder + : X9ECParametersHolder + { + private SecT163K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT163K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new SecT163K1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8" + + "0289070FB05D38FF58321F2E800536D538CCDAA3D9")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect163r1 + */ + internal class SecT163R1Holder + : X9ECParametersHolder + { + private SecT163R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT163R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("24B7B137C8A14D696E6768756151756FD0DA2E5C"); + ECCurve curve = ConfigureCurve(new SecT163R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "0369979697AB43897789566789567F787A7876A654" + + "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect163r2 + */ + internal class SecT163R2Holder + : X9ECParametersHolder + { + private SecT163R2Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT163R2Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("85E25BFE5C86226CDB12016F7553F9D0E693A268"); + ECCurve curve = ConfigureCurve(new SecT163R2Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "03F0EBA16286A2D57EA0991168D4994637E8343E36" + + "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect193r1 + */ + internal class SecT193R1Holder + : X9ECParametersHolder + { + private SecT193R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT193R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("103FAEC74D696E676875615175777FC5B191EF30"); + ECCurve curve = ConfigureCurve(new SecT193R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1" + + "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect193r2 + */ + internal class SecT193R2Holder + : X9ECParametersHolder + { + private SecT193R2Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT193R2Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("10B7B4D696E676875615175137C8A16FD0DA2211"); + ECCurve curve = ConfigureCurve(new SecT193R2Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F" + + "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect233k1 + */ + internal class SecT233K1Holder + : X9ECParametersHolder + { + private SecT233K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT233K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new SecT233K1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126" + + "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect233r1 + */ + internal class SecT233R1Holder + : X9ECParametersHolder + { + private SecT233R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT233R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("74D59FF07F6B413D0EA14B344B20A2DB049B50C3"); + ECCurve curve = ConfigureCurve(new SecT233R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B" + + "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect239k1 + */ + internal class SecT239K1Holder + : X9ECParametersHolder + { + private SecT239K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT239K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new SecT239K1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC" + + "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect283k1 + */ + internal class SecT283K1Holder + : X9ECParametersHolder + { + private SecT283K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT283K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new SecT283K1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836" + + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect283r1 + */ + internal class SecT283R1Holder + : X9ECParametersHolder + { + private SecT283R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT283R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE"); + ECCurve curve = ConfigureCurve(new SecT283R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053" + + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect409k1 + */ + internal class SecT409K1Holder + : X9ECParametersHolder + { + private SecT409K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT409K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new SecT409K1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746" + + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect409r1 + */ + internal class SecT409R1Holder + : X9ECParametersHolder + { + private SecT409R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT409R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("4099B5A457F9D69F79213D094C4BCD4D4262210B"); + ECCurve curve = ConfigureCurve(new SecT409R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7" + + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect571k1 + */ + internal class SecT571K1Holder + : X9ECParametersHolder + { + private SecT571K1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT571K1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new SecT571K1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972" + + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + /* + * sect571r1 + */ + internal class SecT571R1Holder + : X9ECParametersHolder + { + private SecT571R1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new SecT571R1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("2AA058F73A0E33AB486B0F610410C53A7F132310"); + ECCurve curve = ConfigureCurve(new SecT571R1Curve()); + X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04" + + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19" + + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + }; + + private static readonly IDictionary nameToCurve = Platform.CreateHashtable(); private static readonly IDictionary nameToOid = Platform.CreateHashtable(); private static readonly IDictionary oidToCurve = Platform.CreateHashtable(); private static readonly IDictionary oidToName = Platform.CreateHashtable(); + private static readonly IList names = Platform.CreateArrayList(); private static void DefineCurve(string name, X9ECParametersHolder holder) { + names.Add(name); + name = Platform.ToLowerInvariant(name); nameToCurve.Add(name, holder); } - private static void DefineCurve(string name, DerObjectIdentifier oid, X9ECParametersHolder holder) + private static void DefineCurveWithOid(string name, DerObjectIdentifier oid, X9ECParametersHolder holder) { - nameToCurve.Add(name, holder); - nameToOid.Add(name, oid); + names.Add(name); oidToName.Add(oid, name); oidToCurve.Add(oid, holder); + name = Platform.ToLowerInvariant(name); + nameToOid.Add(name, oid); + nameToCurve.Add(name, holder); } - private static void DefineCurveAlias(string alias, DerObjectIdentifier oid) + private static void DefineCurveAlias(string name, DerObjectIdentifier oid) { - alias = Platform.ToLowerInvariant(alias); - nameToOid.Add(alias, oid); - nameToCurve.Add(alias, oidToCurve[oid]); + object curve = oidToCurve[oid]; + if (curve == null) + throw new InvalidOperationException(); + + name = Platform.ToLowerInvariant(name); + nameToOid.Add(name, oid); + nameToCurve.Add(name, curve); } static CustomNamedCurves() { DefineCurve("curve25519", Curve25519Holder.Instance); - DefineCurve("secp192k1", SecObjectIdentifiers.SecP192k1, Secp192k1Holder.Instance); - DefineCurve("secp192r1", SecObjectIdentifiers.SecP192r1, Secp192r1Holder.Instance); - DefineCurve("secp224k1", SecObjectIdentifiers.SecP224k1, Secp224k1Holder.Instance); - DefineCurve("secp224r1", SecObjectIdentifiers.SecP224r1, Secp224r1Holder.Instance); - DefineCurve("secp256k1", SecObjectIdentifiers.SecP256k1, Secp256k1Holder.Instance); - DefineCurve("secp256r1", SecObjectIdentifiers.SecP256r1, Secp256r1Holder.Instance); - DefineCurve("secp384r1", SecObjectIdentifiers.SecP384r1, Secp384r1Holder.Instance); - DefineCurve("secp521r1", SecObjectIdentifiers.SecP521r1, Secp521r1Holder.Instance); + //DefineCurveWithOid("secp112r1", SecObjectIdentifiers.SecP112r1, SecP112R1Holder.Instance); + //DefineCurveWithOid("secp112r2", SecObjectIdentifiers.SecP112r2, SecP112R2Holder.Instance); + DefineCurveWithOid("secp128r1", SecObjectIdentifiers.SecP128r1, SecP128R1Holder.Instance); + //DefineCurveWithOid("secp128r2", SecObjectIdentifiers.SecP128r2, SecP128R2Holder.Instance); + DefineCurveWithOid("secp160k1", SecObjectIdentifiers.SecP160k1, SecP160K1Holder.Instance); + DefineCurveWithOid("secp160r1", SecObjectIdentifiers.SecP160r1, SecP160R1Holder.Instance); + DefineCurveWithOid("secp160r2", SecObjectIdentifiers.SecP160r2, SecP160R2Holder.Instance); + DefineCurveWithOid("secp192k1", SecObjectIdentifiers.SecP192k1, SecP192K1Holder.Instance); + DefineCurveWithOid("secp192r1", SecObjectIdentifiers.SecP192r1, SecP192R1Holder.Instance); + DefineCurveWithOid("secp224k1", SecObjectIdentifiers.SecP224k1, SecP224K1Holder.Instance); + DefineCurveWithOid("secp224r1", SecObjectIdentifiers.SecP224r1, SecP224R1Holder.Instance); + DefineCurveWithOid("secp256k1", SecObjectIdentifiers.SecP256k1, SecP256K1Holder.Instance); + DefineCurveWithOid("secp256r1", SecObjectIdentifiers.SecP256r1, SecP256R1Holder.Instance); + DefineCurveWithOid("secp384r1", SecObjectIdentifiers.SecP384r1, SecP384R1Holder.Instance); + DefineCurveWithOid("secp521r1", SecObjectIdentifiers.SecP521r1, SecP521R1Holder.Instance); + + DefineCurveWithOid("sect113r1", SecObjectIdentifiers.SecT113r1, SecT113R1Holder.Instance); + DefineCurveWithOid("sect113r2", SecObjectIdentifiers.SecT113r2, SecT113R2Holder.Instance); + DefineCurveWithOid("sect131r1", SecObjectIdentifiers.SecT131r1, SecT131R1Holder.Instance); + DefineCurveWithOid("sect131r2", SecObjectIdentifiers.SecT131r2, SecT131R2Holder.Instance); + DefineCurveWithOid("sect163k1", SecObjectIdentifiers.SecT163k1, SecT163K1Holder.Instance); + DefineCurveWithOid("sect163r1", SecObjectIdentifiers.SecT163r1, SecT163R1Holder.Instance); + DefineCurveWithOid("sect163r2", SecObjectIdentifiers.SecT163r2, SecT163R2Holder.Instance); + DefineCurveWithOid("sect193r1", SecObjectIdentifiers.SecT193r1, SecT193R1Holder.Instance); + DefineCurveWithOid("sect193r2", SecObjectIdentifiers.SecT193r2, SecT193R2Holder.Instance); + DefineCurveWithOid("sect233k1", SecObjectIdentifiers.SecT233k1, SecT233K1Holder.Instance); + DefineCurveWithOid("sect233r1", SecObjectIdentifiers.SecT233r1, SecT233R1Holder.Instance); + DefineCurveWithOid("sect239k1", SecObjectIdentifiers.SecT239k1, SecT239K1Holder.Instance); + DefineCurveWithOid("sect283k1", SecObjectIdentifiers.SecT283k1, SecT283K1Holder.Instance); + DefineCurveWithOid("sect283r1", SecObjectIdentifiers.SecT283r1, SecT283R1Holder.Instance); + DefineCurveWithOid("sect409k1", SecObjectIdentifiers.SecT409k1, SecT409K1Holder.Instance); + DefineCurveWithOid("sect409r1", SecObjectIdentifiers.SecT409r1, SecT409R1Holder.Instance); + DefineCurveWithOid("sect571k1", SecObjectIdentifiers.SecT571k1, SecT571K1Holder.Instance); + DefineCurveWithOid("sect571r1", SecObjectIdentifiers.SecT571r1, SecT571R1Holder.Instance); + + DefineCurveAlias("B-163", SecObjectIdentifiers.SecT163r2); + DefineCurveAlias("B-233", SecObjectIdentifiers.SecT233r1); + DefineCurveAlias("B-283", SecObjectIdentifiers.SecT283r1); + DefineCurveAlias("B-409", SecObjectIdentifiers.SecT409r1); + DefineCurveAlias("B-571", SecObjectIdentifiers.SecT571r1); + + DefineCurveAlias("K-163", SecObjectIdentifiers.SecT163k1); + DefineCurveAlias("K-233", SecObjectIdentifiers.SecT233k1); + DefineCurveAlias("K-283", SecObjectIdentifiers.SecT283k1); + DefineCurveAlias("K-409", SecObjectIdentifiers.SecT409k1); + DefineCurveAlias("K-571", SecObjectIdentifiers.SecT571k1); DefineCurveAlias("P-192", SecObjectIdentifiers.SecP192r1); DefineCurveAlias("P-224", SecObjectIdentifiers.SecP224r1); @@ -360,7 +882,7 @@ namespace Org.BouncyCastle.Crypto.EC */ public static IEnumerable Names { - get { return new EnumerableProxy(nameToCurve.Keys); } + get { return new EnumerableProxy(names); } } } } diff --git a/crypto/src/crypto/encodings/Pkcs1Encoding.cs b/crypto/src/crypto/encodings/Pkcs1Encoding.cs
index d2225a7d4..35fd96abe 100644 --- a/crypto/src/crypto/encodings/Pkcs1Encoding.cs +++ b/crypto/src/crypto/encodings/Pkcs1Encoding.cs
@@ -7,226 +7,376 @@ 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 - * <p> - * The static property is checked during construction of the encoding object, it is set to - * true by default. - * </p> - */ - 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) - { - if (inLen > GetInputBlockSize()) - throw new ArgumentException("input data too large", "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; - } - } + /** + * 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 + * <p> + * The static property is checked during construction of the encoding object, it is set to + * true by default. + * </p> + */ + 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; + private int pLen = -1; + private byte[] fallback = null; + + /** + * Basic constructor. + * @param cipher + */ + public Pkcs1Encoding( + IAsymmetricBlockCipher cipher) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + } + + /** + * Constructor for decryption with a fixed plaintext length. + * + * @param cipher The cipher to use for cryptographic operation. + * @param pLen Length of the expected plaintext. + */ + public Pkcs1Encoding(IAsymmetricBlockCipher cipher, int pLen) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + this.pLen = pLen; + } + + /** + * Constructor for decryption with a fixed plaintext length and a fallback + * value that is returned, if the padding is incorrect. + * + * @param cipher + * The cipher to use for cryptographic operation. + * @param fallback + * The fallback value, we don't to a arraycopy here. + */ + public Pkcs1Encoding(IAsymmetricBlockCipher cipher, byte[] fallback) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + this.fallback = fallback; + this.pLen = fallback.Length; + } + + 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) + { + if (inLen > GetInputBlockSize()) + throw new ArgumentException("input data too large", "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); + } + + /** + * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext + * for encryption. + * + * @param encoded The Plaintext. + * @param pLen Expected length of the plaintext. + * @return Either 0, if the encoding is correct, or -1, if it is incorrect. + */ + private static int CheckPkcs1Encoding(byte[] encoded, int pLen) + { + int correct = 0; + /* + * Check if the first two bytes are 0 2 + */ + correct |= (encoded[0] ^ 2); + + /* + * Now the padding check, check for no 0 byte in the padding + */ + int plen = encoded.Length - ( + pLen /* Lenght of the PMS */ + + 1 /* Final 0-byte before PMS */ + ); + + for (int i = 1; i < plen; i++) + { + int tmp = encoded[i]; + tmp |= tmp >> 1; + tmp |= tmp >> 2; + tmp |= tmp >> 4; + correct |= (tmp & 1) - 1; + } + + /* + * Make sure the padding ends with a 0 byte. + */ + correct |= encoded[encoded.Length - (pLen + 1)]; + + /* + * Return 0 or 1, depending on the result. + */ + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + return ~((correct & 1) - 1); + } + + /** + * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct. + * + * @param in The encrypted block. + * @param inOff Offset in the encrypted block. + * @param inLen Length of the encrypted block. + * @param pLen Length of the desired output. + * @return The plaintext without padding, or a random value if the padding was incorrect. + * + * @throws InvalidCipherTextException + */ + private byte[] DecodeBlockOrRandom(byte[] input, int inOff, int inLen) + { + if (!forPrivateKey) + throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing"); + + byte[] block = engine.ProcessBlock(input, inOff, inLen); + byte[] random = null; + if (this.fallback == null) + { + random = new byte[this.pLen]; + this.random.NextBytes(random); + } + else + { + random = fallback; + } + + /* + * TODO: This is a potential dangerous side channel. However, you can + * fix this by changing the RSA engine in a way, that it will always + * return blocks of the same length and prepend them with 0 bytes if + * needed. + */ + if (block.Length < GetOutputBlockSize()) + throw new InvalidCipherTextException("block truncated"); + + /* + * TODO: Potential side channel. Fix it by making the engine always + * return blocks of the correct length. + */ + if (useStrictLength && block.Length != engine.GetOutputBlockSize()) + throw new InvalidCipherTextException("block incorrect size"); + + /* + * Check the padding. + */ + int correct = Pkcs1Encoding.CheckPkcs1Encoding(block, this.pLen); + + /* + * Now, to a constant time constant memory copy of the decrypted value + * or the random value, depending on the validity of the padding. + */ + byte[] result = new byte[this.pLen]; + for (int i = 0; i < this.pLen; i++) + { + result[i] = (byte)((block[i+(block.Length-pLen)]&(~correct)) | (random[i]&correct)); + } + + return result; + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format. + */ + private byte[] DecodeBlock( + byte[] input, + int inOff, + int inLen) + { + /* + * If the length of the expected plaintext is known, we use a constant-time decryption. + * If the decryption fails, we return a random value. + */ + if (this.pLen != -1) + { + return this.DecodeBlockOrRandom(input, inOff, 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/crypto/src/crypto/engines/AesEngine.cs b/crypto/src/crypto/engines/AesEngine.cs
index 47eaada3e..164c43ee9 100644 --- a/crypto/src/crypto/engines/AesEngine.cs +++ b/crypto/src/crypto/engines/AesEngine.cs
@@ -6,288 +6,299 @@ using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Engines { - /** - * an implementation of the AES (Rijndael), from FIPS-197. - * <p> - * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>. - * - * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at - * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a> - * - * 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. - * </p> - * <p> - * This file contains the middle performance version with 2Kbytes of static tables for round precomputation. - * </p> - */ - 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 byte[] 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 uint[] T0 = - { - 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, - 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, - 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, - 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, - 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, - 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, - 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, - 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, - 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, - 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, - 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, - 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, - 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, - 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, - 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, - 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, - 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, - 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, - 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, - 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, - 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, - 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, - 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, - 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, - 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, - 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, - 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, - 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, - 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, - 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, - 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, - 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, - 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, - 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, - 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, - 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, - 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, - 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, - 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, - 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, - 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, - 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, - 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, - 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, - 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, - 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, - 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, - 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, - 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, - 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, - 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, - 0x3a16162c - }; - - private static readonly uint[] Tinv0 = - { - 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, - 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, - 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, - 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, - 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, - 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, - 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, - 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, - 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, - 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, - 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, - 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, - 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, - 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, - 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, - 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, - 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, - 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, - 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, - 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, - 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, - 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, - 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, - 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, - 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, - 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, - 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, - 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, - 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, - 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, - 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, - 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, - 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, - 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, - 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, - 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, - 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, - 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, - 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, - 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, - 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, - 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, - 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, - 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, - 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, - 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, - 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, - 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, - 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, - 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, - 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, - 0x4257b8d0 - }; - - private static uint Shift(uint r, int shift) - { - return (r >> shift) | (r << (32 - shift)); - } - - /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ - - private const uint m1 = 0x80808080; - private const uint m2 = 0x7f7f7f7f; - private const uint m3 = 0x0000001b; - - private static uint FFmulX(uint x) - { - return ((x & m2) << 1) ^ (((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 static uint Inv_Mcol(uint x) - { - uint f2 = FFmulX(x); - uint f4 = FFmulX(f2); - uint f8 = FFmulX(f4); - uint f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); - } - - private static uint SubWord(uint x) - { - return (uint)S[x&255] - | (((uint)S[(x>>8)&255]) << 8) - | (((uint)S[(x>>16)&255]) << 16) - | (((uint)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 uint[][] 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 + /** + * an implementation of the AES (Rijndael), from FIPS-197. + * <p> + * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a> + * + * 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. + * </p> + * <p> + * This file contains the middle performance version with 2Kbytes of static tables for round precomputation. + * </p> + */ + 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 byte[] 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 uint[] T0 = + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c + }; + + private static readonly uint[] Tinv0 = + { + 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, + 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, + 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, + 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, + 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, + 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, + 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, + 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, + 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, + 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, + 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, + 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, + 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, + 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, + 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, + 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, + 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, + 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, + 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, + 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, + 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, + 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, + 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, + 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, + 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, + 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, + 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, + 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, + 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, + 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, + 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, + 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, + 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, + 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, + 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, + 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, + 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, + 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, + 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, + 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, + 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, + 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, + 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, + 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, + 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, + 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, + 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, + 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, + 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, + 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, + 0x4257b8d0 + }; + + private static uint Shift(uint r, int shift) + { + return (r >> shift) | (r << (32 - shift)); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const uint m1 = 0x80808080; + private const uint m2 = 0x7f7f7f7f; + private const uint m3 = 0x0000001b; + private const uint m4 = 0xC0C0C0C0; + private const uint m5 = 0x3f3f3f3f; + + private static uint FFmulX(uint x) + { + return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + } + + private static uint FFmulX2(uint x) + { + uint t0 = (x & m5) << 2; + uint t1 = (x & m4); + t1 ^= (t1 >> 1); + return t0 ^ (t1 >> 2) ^ (t1 >> 5); + } + + /* + 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 static uint Inv_Mcol(uint x) + { + uint t0, t1; + t0 = x; + t1 = t0 ^ Shift(t0, 8); + t0 ^= FFmulX(t1); + t1 ^= FFmulX2(t0); + t0 ^= t1 ^ Shift(t1, 16); + return t0; + } + + private static uint SubWord(uint x) + { + return (uint)S[x&255] + | (((uint)S[(x>>8)&255]) << 8) + | (((uint)S[(x>>16)&255]) << 16) + | (((uint)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 uint[][] 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 uint[][] W = new uint[ROUNDS + 1][]; // 4 words in a block for (int i = 0; i <= ROUNDS; ++i) @@ -295,252 +306,229 @@ namespace Org.BouncyCastle.Crypto.Engines W[i] = new uint[4]; } - // - // copy the key into the round key array - // - - t = 0; - for (int i = 0; i < key.Length; t++) - { - W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); - i+=4; - } - - // - // while not enough round key material calculated - // calculate new values - // - int k = (ROUNDS + 1) << 2; - for (int i = KC; (i < k); i++) - { - uint 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++) - { + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + uint 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++) + { uint[] w = W[j]; for (int i = 0; i < 4; i++) { w[i] = Inv_Mcol(w[i]); } } - } - - return W; - } - - private int ROUNDS; - private uint[][] WorkingKey; - private uint 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(); - } - else - { - DecryptBlock(); - } - - PackBlock(output, outOff); - - return BLOCK_SIZE; - } - - public void Reset() - { - } - - private void UnPackBlock( - byte[] bytes, - int off) - { - C0 = Pack.LE_To_UInt32(bytes, off); - C1 = Pack.LE_To_UInt32(bytes, off + 4); - C2 = Pack.LE_To_UInt32(bytes, off + 8); - C3 = Pack.LE_To_UInt32(bytes, off + 12); - } - - private void PackBlock( - byte[] bytes, - int off) - { - Pack.UInt32_To_LE(C0, bytes, off); - Pack.UInt32_To_LE(C1, bytes, off + 4); - Pack.UInt32_To_LE(C2, bytes, off + 8); - Pack.UInt32_To_LE(C3, bytes, off + 12); - } - - private void EncryptBlock() - { - uint[][] KW = WorkingKey; - - int r = 0; - uint[] kw = KW[r]; - - C0 ^= kw[0]; - C1 ^= kw[1]; - C2 ^= kw[2]; - C3 ^= kw[3]; - - uint r0, r1, r2, r3; - - while (r < (ROUNDS - 2)) - { - kw = KW[++r]; - r0 = T0[C0 & 255] ^ Shift(T0[(C1 >> 8) & 255], 24) ^ Shift(T0[(C2 >> 16) & 255], 16) ^ Shift(T0[(C3 >> 24) & 255], 8) ^ kw[0]; - r1 = T0[C1 & 255] ^ Shift(T0[(C2 >> 8) & 255], 24) ^ Shift(T0[(C3 >> 16) & 255], 16) ^ Shift(T0[(C0 >> 24) & 255], 8) ^ kw[1]; - r2 = T0[C2 & 255] ^ Shift(T0[(C3 >> 8) & 255], 24) ^ Shift(T0[(C0 >> 16) & 255], 16) ^ Shift(T0[(C1 >> 24) & 255], 8) ^ kw[2]; - r3 = T0[C3 & 255] ^ Shift(T0[(C0 >> 8) & 255], 24) ^ Shift(T0[(C1 >> 16) & 255], 16) ^ Shift(T0[(C2 >> 24) & 255], 8) ^ kw[3]; - kw = KW[++r]; - C0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; - C1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1]; - C2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2]; - C3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3]; - } - - kw = KW[++r]; - r0 = T0[C0 & 255] ^ Shift(T0[(C1 >> 8) & 255], 24) ^ Shift(T0[(C2 >> 16) & 255], 16) ^ Shift(T0[(C3 >> 24) & 255], 8) ^ kw[0]; - r1 = T0[C1 & 255] ^ Shift(T0[(C2 >> 8) & 255], 24) ^ Shift(T0[(C3 >> 16) & 255], 16) ^ Shift(T0[(C0 >> 24) & 255], 8) ^ kw[1]; - r2 = T0[C2 & 255] ^ Shift(T0[(C3 >> 8) & 255], 24) ^ Shift(T0[(C0 >> 16) & 255], 16) ^ Shift(T0[(C1 >> 24) & 255], 8) ^ kw[2]; - r3 = T0[C3 & 255] ^ Shift(T0[(C0 >> 8) & 255], 24) ^ Shift(T0[(C1 >> 16) & 255], 16) ^ Shift(T0[(C2 >> 24) & 255], 8) ^ kw[3]; - - // the final round's table is a simple function of S so we don't use a whole other four tables for it - - kw = KW[++r]; - C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; - C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; - C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; - C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; - - Debug.Assert(r == ROUNDS); + } + + return W; } - private void DecryptBlock() - { - uint[][] KW = WorkingKey; - - int r = ROUNDS; - uint[] kw = KW[r]; - - C0 ^= kw[0]; - C1 ^= kw[1]; - C2 ^= kw[2]; - C3 ^= kw[3]; - - uint r0, r1, r2, r3; - - while (r > 2) - { - kw = KW[--r]; - r0 = Tinv0[C0 & 255] ^ Shift(Tinv0[(C3 >> 8) & 255], 24) ^ Shift(Tinv0[(C2 >> 16) & 255], 16) ^ Shift(Tinv0[(C1 >> 24) & 255], 8) ^ kw[0]; - r1 = Tinv0[C1 & 255] ^ Shift(Tinv0[(C0 >> 8) & 255], 24) ^ Shift(Tinv0[(C3 >> 16) & 255], 16) ^ Shift(Tinv0[(C2 >> 24) & 255], 8) ^ kw[1]; - r2 = Tinv0[C2 & 255] ^ Shift(Tinv0[(C1 >> 8) & 255], 24) ^ Shift(Tinv0[(C0 >> 16) & 255], 16) ^ Shift(Tinv0[(C3 >> 24) & 255], 8) ^ kw[2]; - r3 = Tinv0[C3 & 255] ^ Shift(Tinv0[(C2 >> 8) & 255], 24) ^ Shift(Tinv0[(C1 >> 16) & 255], 16) ^ Shift(Tinv0[(C0 >> 24) & 255], 8) ^ kw[3]; - kw = KW[--r]; - C0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0]; - C1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1]; - C2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; - C3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3]; - } - - kw = KW[--r]; - r0 = Tinv0[C0 & 255] ^ Shift(Tinv0[(C3 >> 8) & 255], 24) ^ Shift(Tinv0[(C2 >> 16) & 255], 16) ^ Shift(Tinv0[(C1 >> 24) & 255], 8) ^ kw[0]; - r1 = Tinv0[C1 & 255] ^ Shift(Tinv0[(C0 >> 8) & 255], 24) ^ Shift(Tinv0[(C3 >> 16) & 255], 16) ^ Shift(Tinv0[(C2 >> 24) & 255], 8) ^ kw[1]; - r2 = Tinv0[C2 & 255] ^ Shift(Tinv0[(C1 >> 8) & 255], 24) ^ Shift(Tinv0[(C0 >> 16) & 255], 16) ^ Shift(Tinv0[(C3 >> 24) & 255], 8) ^ kw[2]; - r3 = Tinv0[C3 & 255] ^ Shift(Tinv0[(C2 >> 8) & 255], 24) ^ Shift(Tinv0[(C1 >> 16) & 255], 16) ^ Shift(Tinv0[(C0 >> 24) & 255], 8) ^ kw[3]; - - // the final round's table is a simple function of Si so we don't use a whole other four tables for it - - kw = KW[--r]; - C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; - C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; - C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; - C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; - - Debug.Assert(r == 0); + private int ROUNDS; + private uint[][] WorkingKey; + private uint 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 virtual 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 virtual string AlgorithmName + { + get { return "AES"; } + } + + public virtual 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("AES engine not initialised"); + + Check.DataLength(input, inOff, 16, "input buffer too short"); + Check.OutputLength(output, outOff, 16, "output buffer too short"); + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(WorkingKey); + } + else + { + DecryptBlock(WorkingKey); + } + + PackBlock(output, outOff); + + return BLOCK_SIZE; + } + + public virtual void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + C0 = Pack.LE_To_UInt32(bytes, off); + C1 = Pack.LE_To_UInt32(bytes, off + 4); + C2 = Pack.LE_To_UInt32(bytes, off + 8); + C3 = Pack.LE_To_UInt32(bytes, off + 12); + } + + private void PackBlock( + byte[] bytes, + int off) + { + Pack.UInt32_To_LE(C0, bytes, off); + Pack.UInt32_To_LE(C1, bytes, off + 4); + Pack.UInt32_To_LE(C2, bytes, off + 8); + Pack.UInt32_To_LE(C3, bytes, off + 12); + } + + private void EncryptBlock(uint[][] KW) + { + uint[] kw = KW[0]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1]; + r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3]; + kw = KW[r++]; + t0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + t1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1]; + t2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3]; + } + + kw = KW[r++]; + r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1]; + r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3]; + + // the final round's table is a simple function of S so we don't use a whole other four tables for it + + kw = KW[r]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + } + + private void DecryptBlock(uint[][] KW) + { + uint[] kw = KW[ROUNDS]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3]; + kw = KW[r--]; + t0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0]; + t1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1]; + t2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3]; + } + + kw = KW[1]; + r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3]; + + // the final round's table is a simple function of Si so we don't use a whole other four tables for it + + kw = KW[0]; + this.C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; } - } + } } diff --git a/crypto/src/crypto/engines/AesFastEngine.cs b/crypto/src/crypto/engines/AesFastEngine.cs
index d76955dd8..38ce1a946 100644 --- a/crypto/src/crypto/engines/AesFastEngine.cs +++ b/crypto/src/crypto/engines/AesFastEngine.cs
@@ -31,11 +31,11 @@ namespace Org.BouncyCastle.Crypto.Engines * </p> */ public class AesFastEngine - : IBlockCipher + : 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, @@ -72,511 +72,521 @@ namespace Org.BouncyCastle.Crypto.Engines // 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)) + { + 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 byte[] 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 uint[] T0 = - { - 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, - 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, - 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, - 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, - 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, - 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, - 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, - 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, - 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, - 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, - 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, - 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, - 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, - 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, - 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, - 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, - 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, - 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, - 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, - 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, - 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, - 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, - 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, - 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, - 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, - 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, - 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, - 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, - 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, - 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, - 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, - 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, - 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, - 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, - 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, - 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, - 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, - 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, - 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, - 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, - 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, - 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, - 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, - 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, - 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, - 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, - 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, - 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, - 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, - 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, - 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, - 0x3a16162c - }; - - private static readonly uint[] T1 = - { - 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, - 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, - 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, - 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, - 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, - 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, - 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, - 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, - 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, - 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, - 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, - 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, - 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, - 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, - 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, - 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, - 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, - 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, - 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, - 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, - 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, - 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, - 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, - 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, - 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, - 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, - 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, - 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, - 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, - 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, - 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, - 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, - 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, - 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, - 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, - 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, - 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, - 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, - 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, - 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, - 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, - 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, - 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, - 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, - 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, - 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, - 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, - 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, - 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, - 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, - 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, - 0x16162c3a - }; - - private static readonly uint[] T2 = - { - 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, - 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, - 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, - 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, - 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, - 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, - 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, - 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, - 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, - 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, - 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, - 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, - 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, - 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, - 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, - 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, - 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, - 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, - 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, - 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, - 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, - 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, - 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, - 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, - 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, - 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, - 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, - 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, - 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, - 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, - 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, - 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, - 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, - 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, - 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, - 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, - 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, - 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, - 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, - 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, - 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, - 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, - 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, - 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, - 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, - 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, - 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, - 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, - 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, - 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, - 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, - 0x162c3a16 - }; - - private static readonly uint[] T3 = - { - 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, - 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, - 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, - 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, - 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, - 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, - 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, - 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, - 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, - 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, - 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, - 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, - 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, - 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, - 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, - 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, - 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, - 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, - 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, - 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, - 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, - 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, - 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, - 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, - 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, - 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, - 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, - 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, - 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, - 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, - 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, - 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, - 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, - 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, - 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, - 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, - 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, - 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, - 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, - 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, - 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, - 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, - 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, - 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, - 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, - 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, - 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, - 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, - 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, - 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, - 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, - 0x2c3a1616 - }; - - private static readonly uint[] Tinv0 = - { - 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, - 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, - 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, - 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, - 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, - 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, - 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, - 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, - 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, - 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, - 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, - 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, - 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, - 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, - 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, - 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, - 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, - 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, - 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, - 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, - 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, - 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, - 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, - 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, - 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, - 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, - 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, - 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, - 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, - 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, - 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, - 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, - 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, - 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, - 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, - 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, - 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, - 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, - 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, - 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, - 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, - 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, - 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, - 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, - 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, - 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, - 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, - 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, - 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, - 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, - 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, - 0x4257b8d0 - }; - - private static readonly uint[] Tinv1 = - { - 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, - 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, - 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, - 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, - 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, - 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, - 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, - 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, - 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, - 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, - 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, - 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, - 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, - 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, - 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, - 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, - 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, - 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, - 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, - 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, - 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, - 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, - 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, - 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, - 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b, - 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, - 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, - 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, - 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, - 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, - 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, - 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, - 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, - 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, - 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, - 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, - 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, - 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, - 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4, - 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, - 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, - 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, - 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, - 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, - 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, - 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, - 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, - 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, - 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, - 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de, - 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, - 0x57b8d042 - }; - - private static readonly uint[] Tinv2 = - { - 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, - 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, - 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, - 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, - 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, - 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, - 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, - 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, - 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, - 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, - 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, - 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, - 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, - 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be, - 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, - 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, - 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, - 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, - 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, - 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, - 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff, - 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, - 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296, - 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, - 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d, - 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, - 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b, - 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, - 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, - 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, - 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, - 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, - 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, - 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, - 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, - 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, - 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, - 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, - 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, - 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, - 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98, - 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, - 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, - 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, - 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, - 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, - 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, - 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, - 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, - 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3, - 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, - 0xb8d04257 - }; - - private static readonly uint[] Tinv3 = - { - 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, - 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, - 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, - 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, - 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f, - 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, - 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, - 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, - 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, - 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, - 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, - 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, - 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, - 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, - 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, - 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, - 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, - 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, - 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, - 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, - 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, - 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, - 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee, - 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, - 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, - 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, - 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, - 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, - 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, - 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, - 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, - 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, - 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, - 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, - 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, - 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, - 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, - 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, - 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, - 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, - 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, - 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, - 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, - 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, - 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, - 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, - 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, - 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, - 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, - 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c, - 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, - 0xd04257b8 - }; + { + 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 uint[] T0 = + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c + }; + + private static readonly uint[] T1 = + { + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, + 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, + 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, + 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, + 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, + 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, + 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, + 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, + 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, + 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, + 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, + 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, + 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, + 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, + 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, + 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, + 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, + 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, + 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, + 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, + 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, + 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, + 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, + 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, + 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, + 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, + 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, + 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, + 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, + 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, + 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, + 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, + 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, + 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, + 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, + 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, + 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, + 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, + 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, + 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, + 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, + 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, + 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, + 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, + 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, + 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, + 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, + 0x16162c3a + }; + + private static readonly uint[] T2 = + { + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, + 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, + 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, + 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, + 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, + 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, + 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, + 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, + 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, + 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, + 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, + 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, + 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, + 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, + 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, + 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, + 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, + 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, + 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, + 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, + 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, + 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, + 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, + 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, + 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, + 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, + 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, + 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, + 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, + 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, + 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, + 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, + 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, + 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, + 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, + 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, + 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, + 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, + 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, + 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, + 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, + 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, + 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, + 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, + 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, + 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, + 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, + 0x162c3a16 + }; + + private static readonly uint[] T3 = + { + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, + 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, + 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, + 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, + 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, + 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, + 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, + 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, + 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, + 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, + 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, + 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, + 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, + 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, + 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, + 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, + 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, + 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, + 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, + 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, + 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, + 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, + 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, + 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, + 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, + 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, + 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, + 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, + 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, + 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, + 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, + 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, + 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, + 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, + 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, + 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, + 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, + 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, + 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, + 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, + 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, + 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, + 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, + 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, + 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, + 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, + 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, + 0x2c3a1616 + }; + + private static readonly uint[] Tinv0 = + { + 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, + 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, + 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, + 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, + 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, + 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, + 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, + 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, + 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, + 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, + 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, + 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, + 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, + 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, + 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, + 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, + 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, + 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, + 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, + 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, + 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, + 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, + 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, + 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, + 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, + 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, + 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, + 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, + 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, + 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, + 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, + 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, + 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, + 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, + 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, + 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, + 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, + 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, + 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, + 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, + 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, + 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, + 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, + 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, + 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, + 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, + 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, + 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, + 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, + 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, + 0x4257b8d0 + }; + + private static readonly uint[] Tinv1 = + { + 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, + 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, + 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, + 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, + 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, + 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, + 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, + 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, + 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, + 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, + 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, + 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, + 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, + 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, + 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, + 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, + 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, + 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, + 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, + 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, + 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, + 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, + 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, + 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, + 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b, + 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, + 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, + 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, + 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, + 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, + 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, + 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, + 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, + 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, + 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, + 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, + 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, + 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, + 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4, + 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, + 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, + 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, + 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, + 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, + 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, + 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, + 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, + 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, + 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, + 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de, + 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, + 0x57b8d042 + }; + + private static readonly uint[] Tinv2 = + { + 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, + 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, + 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, + 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, + 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, + 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, + 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, + 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, + 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, + 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, + 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, + 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, + 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, + 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be, + 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, + 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, + 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, + 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, + 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, + 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, + 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff, + 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, + 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296, + 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, + 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d, + 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, + 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b, + 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, + 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, + 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, + 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, + 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, + 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, + 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, + 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, + 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, + 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, + 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, + 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, + 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, + 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98, + 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, + 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, + 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, + 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, + 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, + 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, + 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, + 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, + 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3, + 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, + 0xb8d04257 + }; + + private static readonly uint[] Tinv3 = + { + 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, + 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, + 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, + 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, + 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f, + 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, + 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, + 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, + 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, + 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, + 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, + 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, + 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, + 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, + 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, + 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, + 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, + 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, + 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, + 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, + 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, + 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, + 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee, + 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, + 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, + 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, + 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, + 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, + 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, + 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, + 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, + 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, + 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, + 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, + 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, + 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, + 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, + 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, + 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, + 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, + 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, + 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, + 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, + 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, + 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, + 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, + 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, + 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, + 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, + 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c, + 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, + 0xd04257b8 + }; private static uint Shift(uint r, int shift) - { - return (r >> shift) | (r << (32 - shift)); - } + { + return (r >> shift) | (r << (32 - shift)); + } /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ private const uint m1 = 0x80808080; private const uint m2 = 0x7f7f7f7f; private const uint m3 = 0x0000001b; + private const uint m4 = 0xC0C0C0C0; + private const uint m5 = 0x3f3f3f3f; + + private static uint FFmulX(uint x) + { + return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + } - private static uint FFmulX(uint x) - { - return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + private static uint FFmulX2(uint x) + { + uint t0 = (x & m5) << 2; + uint t1 = (x & m4); + t1 ^= (t1 >> 1); + return t0 ^ (t1 >> 2) ^ (t1 >> 5); } /* @@ -591,20 +601,21 @@ namespace Org.BouncyCastle.Crypto.Engines private static uint Inv_Mcol(uint x) { - uint f2 = FFmulX(x); - uint f4 = FFmulX(f2); - uint f8 = FFmulX(f4); - uint f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + uint t0, t1; + t0 = x; + t1 = t0 ^ Shift(t0, 8); + t0 ^= FFmulX(t1); + t1 ^= FFmulX2(t0); + t0 ^= t1 ^ Shift(t1, 16); + return t0; } private static uint SubWord(uint x) - { - return (uint)S[x&255] - | (((uint)S[(x>>8)&255]) << 8) - | (((uint)S[(x>>16)&255]) << 16) - | (((uint)S[(x>>24)&255]) << 24); + { + return (uint)S[x&255] + | (((uint)S[(x>>8)&255]) << 8) + | (((uint)S[(x>>16)&255]) << 16) + | (((uint)S[(x>>24)&255]) << 24); } /** @@ -622,7 +633,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) 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 + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes uint[][] W = new uint[ROUNDS + 1][]; // 4 words in a block for (int i = 0; i <= ROUNDS; ++i) @@ -636,10 +647,10 @@ namespace Org.BouncyCastle.Crypto.Engines int t = 0; for (int i = 0; i < key.Length; t++) - { - W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); - i+=4; - } + { + W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } // // while not enough round key material calculated @@ -659,12 +670,12 @@ namespace Org.BouncyCastle.Crypto.Engines } if (!forEncryption) - { + { for (int j = 1; j < ROUNDS; j++) - { + { uint[] w = W[j]; for (int i = 0; i < 4; i++) - { + { w[i] = Inv_Mcol(w[i]); } } @@ -695,7 +706,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -709,59 +720,50 @@ namespace Org.BouncyCastle.Crypto.Engines this.forEncryption = forEncryption; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "AES"; } } - public bool IsPartialBlockOkay - { - get { return false; } - } + public virtual bool IsPartialBlockOkay + { + get { return false; } + } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } - public int ProcessBlock( + public virtual 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"); - } + Check.DataLength(input, inOff, 16, "input buffer too short"); + Check.OutputLength(output, outOff, 16, "output buffer too short"); UnPackBlock(input, inOff); if (forEncryption) { - EncryptBlock(); + EncryptBlock(WorkingKey); } else { - DecryptBlock(); + DecryptBlock(WorkingKey); } PackBlock(output, outOff); - return BLOCK_SIZE; + return BLOCK_SIZE; } - public void Reset() + public virtual void Reset() { } @@ -769,110 +771,96 @@ namespace Org.BouncyCastle.Crypto.Engines byte[] bytes, int off) { - C0 = Pack.LE_To_UInt32(bytes, off); - C1 = Pack.LE_To_UInt32(bytes, off + 4); - C2 = Pack.LE_To_UInt32(bytes, off + 8); - C3 = Pack.LE_To_UInt32(bytes, off + 12); + C0 = Pack.LE_To_UInt32(bytes, off); + C1 = Pack.LE_To_UInt32(bytes, off + 4); + C2 = Pack.LE_To_UInt32(bytes, off + 8); + C3 = Pack.LE_To_UInt32(bytes, off + 12); } - private void PackBlock( + private void PackBlock( byte[] bytes, int off) { - Pack.UInt32_To_LE(C0, bytes, off); - Pack.UInt32_To_LE(C1, bytes, off + 4); - Pack.UInt32_To_LE(C2, bytes, off + 8); - Pack.UInt32_To_LE(C3, bytes, off + 12); + Pack.UInt32_To_LE(C0, bytes, off); + Pack.UInt32_To_LE(C1, bytes, off + 4); + Pack.UInt32_To_LE(C2, bytes, off + 8); + Pack.UInt32_To_LE(C3, bytes, off + 12); } - private void EncryptBlock() + private void EncryptBlock(uint[][] KW) { - uint[][] KW = WorkingKey; - - int r = 0; - uint[] kw = KW[r]; - - C0 ^= kw[0]; - C1 ^= kw[1]; - C2 ^= kw[2]; - C3 ^= kw[3]; - - uint r0, r1, r2, r3; - - while (r < (ROUNDS - 2)) - { - kw = KW[++r]; - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ kw[0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ kw[1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ kw[2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ kw[3]; - kw = KW[++r]; - C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; - C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ kw[1]; - C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ kw[2]; - C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ kw[3]; + uint[] kw = KW[0]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = T0[t0 & 255] ^ T1[(t1 >> 8) & 255] ^ T2[(t2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; + r1 = T0[t1 & 255] ^ T1[(t2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[t0 >> 24] ^ kw[1]; + r2 = T0[t2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(t0 >> 16) & 255] ^ T3[t1 >> 24] ^ kw[2]; + r3 = T0[r3 & 255] ^ T1[(t0 >> 8) & 255] ^ T2[(t1 >> 16) & 255] ^ T3[t2 >> 24] ^ kw[3]; + kw = KW[r++]; + t0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; + t1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ kw[1]; + t2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ kw[2]; + r3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ kw[3]; } - kw = KW[++r]; - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ kw[0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ kw[1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ kw[2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ kw[3]; + kw = KW[r++]; + r0 = T0[t0 & 255] ^ T1[(t1 >> 8) & 255] ^ T2[(t2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; + r1 = T0[t1 & 255] ^ T1[(t2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[t0 >> 24] ^ kw[1]; + r2 = T0[t2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(t0 >> 16) & 255] ^ T3[t1 >> 24] ^ kw[2]; + r3 = T0[r3 & 255] ^ T1[(t0 >> 8) & 255] ^ T2[(t1 >> 16) & 255] ^ T3[t2 >> 24] ^ kw[3]; // the final round's table is a simple function of S so we don't use a whole other four tables for it - kw = KW[++r]; - C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ kw[0]; - C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ kw[1]; - C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ kw[2]; - C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ kw[3]; - - Debug.Assert(r == ROUNDS); + kw = KW[r]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ kw[0]; + this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ kw[1]; + this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ kw[2]; + this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ kw[3]; } - private void DecryptBlock() + private void DecryptBlock(uint[][] KW) { - uint[][] KW = WorkingKey; - - int r = ROUNDS; - uint[] kw = KW[r]; - - C0 ^= kw[0]; - C1 ^= kw[1]; - C2 ^= kw[2]; - C3 ^= kw[3]; - - uint r0, r1, r2, r3; - - while (r > 2) + uint[] kw = KW[ROUNDS]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) { - kw = KW[--r]; - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ kw[0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ kw[1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ kw[2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ kw[3]; - kw = KW[--r]; - C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ kw[0]; - C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ kw[1]; - C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; - C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ kw[3]; + kw = KW[r--]; + r0 = Tinv0[t0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(t2 >> 16) & 255] ^ Tinv3[t1 >> 24] ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Tinv1[(t0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[t2 >> 24] ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Tinv1[(t1 >> 8) & 255] ^ Tinv2[(t0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Tinv1[(t2 >> 8) & 255] ^ Tinv2[(t1 >> 16) & 255] ^ Tinv3[t0 >> 24] ^ kw[3]; + kw = KW[r--]; + t0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ kw[0]; + t1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ kw[1]; + t2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ kw[3]; } - kw = KW[--r]; - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ kw[0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ kw[1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ kw[2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ kw[3]; + kw = KW[1]; + r0 = Tinv0[t0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(t2 >> 16) & 255] ^ Tinv3[t1 >> 24] ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Tinv1[(t0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[t2 >> 24] ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Tinv1[(t1 >> 8) & 255] ^ Tinv2[(t0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Tinv1[(t2 >> 8) & 255] ^ Tinv2[(t1 >> 16) & 255] ^ Tinv3[t0 >> 24] ^ kw[3]; // the final round's table is a simple function of Si so we don't use a whole other four tables for it - kw = KW[--r]; - C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ kw[0]; - C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ kw[1]; - C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ kw[2]; - C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ kw[3]; - - Debug.Assert(r == 0); - } + kw = KW[0]; + this.C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ kw[0]; + this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ kw[1]; + this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ kw[3]; + } } } diff --git a/crypto/src/crypto/engines/AesLightEngine.cs b/crypto/src/crypto/engines/AesLightEngine.cs
index 828ceaa33..a42b34971 100644 --- a/crypto/src/crypto/engines/AesLightEngine.cs +++ b/crypto/src/crypto/engines/AesLightEngine.cs
@@ -6,183 +6,196 @@ using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Engines { - /** - * an implementation of the AES (Rijndael), from FIPS-197. - * <p> - * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>. - * - * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at - * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a> - * - * 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. - * </p> - * <p> - * This file contains the slowest performance version with no static tables - * for round precomputation, but it has the smallest foot print. - * </p> - */ - public class AesLightEngine - : 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 byte[] 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 static uint Shift(uint r, int shift) - { - return (r >> shift) | (r << (32 - shift)); - } - - /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ - - private const uint m1 = 0x80808080; - private const uint m2 = 0x7f7f7f7f; - private const uint m3 = 0x0000001b; - - private static uint FFmulX(uint x) - { - return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); - } + /** + * an implementation of the AES (Rijndael), from FIPS-197. + * <p> + * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a> + * + * 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. + * </p> + * <p> + * This file contains the slowest performance version with no static tables + * for round precomputation, but it has the smallest foot print. + * </p> + */ + public class AesLightEngine + : 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 byte[] 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 static uint Shift(uint r, int shift) + { + return (r >> shift) | (r << (32 - shift)); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const uint m1 = 0x80808080; + private const uint m2 = 0x7f7f7f7f; + private const uint m3 = 0x0000001b; + private const uint m4 = 0xC0C0C0C0; + private const uint m5 = 0x3f3f3f3f; + + private static uint FFmulX(uint x) + { + return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + } + + private static uint FFmulX2(uint x) + { + uint t0 = (x & m5) << 2; + uint t1 = (x & m4); + t1 ^= (t1 >> 1); + return t0 ^ (t1 >> 2) ^ (t1 >> 5); + } /* - 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 static uint Mcol(uint x) - { - uint f2 = FFmulX(x); - return f2 ^ Shift(x ^ f2, 8) ^ Shift(x, 16) ^ Shift(x, 24); - } - - private static uint Inv_Mcol(uint x) - { - uint f2 = FFmulX(x); - uint f4 = FFmulX(f2); - uint f8 = FFmulX(f4); - uint f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); - } - - private static uint SubWord(uint x) - { - return (uint)S[x&255] - | (((uint)S[(x>>8)&255]) << 8) - | (((uint)S[(x>>16)&255]) << 16) - | (((uint)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 uint[][] 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 + 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 static uint Mcol(uint x) + { + uint t0, t1; + t0 = Shift(x, 8); + t1 = x ^ t0; + return Shift(t1, 16) ^ t0 ^ FFmulX(t1); + } + + private static uint Inv_Mcol(uint x) + { + uint t0, t1; + t0 = x; + t1 = t0 ^ Shift(t0, 8); + t0 ^= FFmulX(t1); + t1 ^= FFmulX2(t0); + t0 ^= t1 ^ Shift(t1, 16); + return t0; + } + + private static uint SubWord(uint x) + { + return (uint)S[x&255] + | (((uint)S[(x>>8)&255]) << 8) + | (((uint)S[(x>>16)&255]) << 16) + | (((uint)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 uint[][] 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 uint[][] W = new uint[ROUNDS + 1][]; // 4 words in a block for (int i = 0; i <= ROUNDS; ++i) @@ -190,78 +203,78 @@ namespace Org.BouncyCastle.Crypto.Engines W[i] = new uint[4]; } - // - // copy the key into the round key array - // - - t = 0; - for (int i = 0; i < key.Length; t++) - { - W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); - i+=4; - } - - // - // while not enough round key material calculated - // calculate new values - // - int k = (ROUNDS + 1) << 2; - for (int i = KC; (i < k); i++) - { - uint 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++) - { + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + uint 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++) + { uint[] w = W[j]; for (int i = 0; i < 4; i++) { w[i] = Inv_Mcol(w[i]); } - } - } - - return W; - } - - private int ROUNDS; - private uint[][] WorkingKey; - private uint 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) - { + } + } + + return W; + } + + private int ROUNDS; + private uint[][] WorkingKey; + private uint 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 virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { KeyParameter keyParameter = parameters as KeyParameter; if (keyParameter == null) @@ -272,170 +285,147 @@ namespace Org.BouncyCastle.Crypto.Engines 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(); - PackBlock(output, outOff); - } - else - { - UnPackBlock(input, inOff); - DecryptBlock(); - PackBlock(output, outOff); - } - - return BLOCK_SIZE; - } - - public void Reset() - { - } - - private void UnPackBlock( - byte[] bytes, - int off) - { - C0 = Pack.LE_To_UInt32(bytes, off); - C1 = Pack.LE_To_UInt32(bytes, off + 4); - C2 = Pack.LE_To_UInt32(bytes, off + 8); - C3 = Pack.LE_To_UInt32(bytes, off + 12); - } - - private void PackBlock( - byte[] bytes, - int off) - { - Pack.UInt32_To_LE(C0, bytes, off); - Pack.UInt32_To_LE(C1, bytes, off + 4); - Pack.UInt32_To_LE(C2, bytes, off + 8); - Pack.UInt32_To_LE(C3, bytes, off + 12); - } - - private void EncryptBlock() - { - uint[][] KW = WorkingKey; - - int r = 0; - uint[] kw = KW[r]; - - C0 ^= kw[0]; - C1 ^= kw[1]; - C2 ^= kw[2]; - C3 ^= kw[3]; - - uint r0, r1, r2, r3; - - while (r < (ROUNDS - 2)) - { - kw = KW[++r]; - r0 = Mcol((uint)S[C0 & 255] ^ (((uint)S[(C1 >> 8) & 255]) << 8) ^ (((uint)S[(C2 >> 16) & 255]) << 16) ^ (((uint)S[(C3 >> 24) & 255]) << 24)) ^ kw[0]; - r1 = Mcol((uint)S[C1 & 255] ^ (((uint)S[(C2 >> 8) & 255]) << 8) ^ (((uint)S[(C3 >> 16) & 255]) << 16) ^ (((uint)S[(C0 >> 24) & 255]) << 24)) ^ kw[1]; - r2 = Mcol((uint)S[C2 & 255] ^ (((uint)S[(C3 >> 8) & 255]) << 8) ^ (((uint)S[(C0 >> 16) & 255]) << 16) ^ (((uint)S[(C1 >> 24) & 255]) << 24)) ^ kw[2]; - r3 = Mcol((uint)S[C3 & 255] ^ (((uint)S[(C0 >> 8) & 255]) << 8) ^ (((uint)S[(C1 >> 16) & 255]) << 16) ^ (((uint)S[(C2 >> 24) & 255]) << 24)) ^ kw[3]; - kw = KW[++r]; - C0 = Mcol((uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; - C1 = Mcol((uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24)) ^ kw[1]; - C2 = Mcol((uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24)) ^ kw[2]; - C3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24)) ^ kw[3]; - } - - kw = KW[++r]; - r0 = Mcol((uint)S[C0 & 255] ^ (((uint)S[(C1 >> 8) & 255]) << 8) ^ (((uint)S[(C2 >> 16) & 255]) << 16) ^ (((uint)S[(C3 >> 24) & 255]) << 24)) ^ kw[0]; - r1 = Mcol((uint)S[C1 & 255] ^ (((uint)S[(C2 >> 8) & 255]) << 8) ^ (((uint)S[(C3 >> 16) & 255]) << 16) ^ (((uint)S[(C0 >> 24) & 255]) << 24)) ^ kw[1]; - r2 = Mcol((uint)S[C2 & 255] ^ (((uint)S[(C3 >> 8) & 255]) << 8) ^ (((uint)S[(C0 >> 16) & 255]) << 16) ^ (((uint)S[(C1 >> 24) & 255]) << 24)) ^ kw[2]; - r3 = Mcol((uint)S[C3 & 255] ^ (((uint)S[(C0 >> 8) & 255]) << 8) ^ (((uint)S[(C1 >> 16) & 255]) << 16) ^ (((uint)S[(C2 >> 24) & 255]) << 24)) ^ kw[3]; - - // the final round is a simple function of S - - kw = KW[++r]; - C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; - C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; - C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; - C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; - - Debug.Assert(r == ROUNDS); + public virtual string AlgorithmName + { + get { return "AES"; } } - private void DecryptBlock() - { - uint[][] KW = WorkingKey; - - int r = ROUNDS; - uint[] kw = KW[r]; - - C0 ^= kw[0]; - C1 ^= kw[1]; - C2 ^= kw[2]; - C3 ^= kw[3]; - - uint r0, r1, r2, r3; - - while (r > 2) - { - kw = KW[--r]; - r0 = Inv_Mcol((uint)Si[C0 & 255] ^ (((uint)Si[(C3 >> 8) & 255]) << 8) ^ (((uint)Si[(C2 >> 16) & 255]) << 16) ^ ((uint)Si[(C1 >> 24) & 255] << 24)) ^ kw[0]; - r1 = Inv_Mcol((uint)Si[C1 & 255] ^ (((uint)Si[(C0 >> 8) & 255]) << 8) ^ (((uint)Si[(C3 >> 16) & 255]) << 16) ^ ((uint)Si[(C2 >> 24) & 255] << 24)) ^ kw[1]; - r2 = Inv_Mcol((uint)Si[C2 & 255] ^ (((uint)Si[(C1 >> 8) & 255]) << 8) ^ (((uint)Si[(C0 >> 16) & 255]) << 16) ^ ((uint)Si[(C3 >> 24) & 255] << 24)) ^ kw[2]; - r3 = Inv_Mcol((uint)Si[C3 & 255] ^ (((uint)Si[(C2 >> 8) & 255]) << 8) ^ (((uint)Si[(C1 >> 16) & 255]) << 16) ^ ((uint)Si[(C0 >> 24) & 255] << 24)) ^ kw[3]; - kw = KW[--r]; - C0 = Inv_Mcol((uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ ((uint)Si[(r1 >> 24) & 255] << 24)) ^ kw[0]; - C1 = Inv_Mcol((uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(r2 >> 24) & 255] << 24)) ^ kw[1]; - C2 = Inv_Mcol((uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; - C3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ ((uint)Si[(r0 >> 24) & 255] << 24)) ^ kw[3]; - } - - kw = KW[--r]; - r0 = Inv_Mcol((uint)Si[C0 & 255] ^ (((uint)Si[(C3 >> 8) & 255]) << 8) ^ (((uint)Si[(C2 >> 16) & 255]) << 16) ^ ((uint)Si[(C1 >> 24) & 255] << 24)) ^ kw[0]; - r1 = Inv_Mcol((uint)Si[C1 & 255] ^ (((uint)Si[(C0 >> 8) & 255]) << 8) ^ (((uint)Si[(C3 >> 16) & 255]) << 16) ^ ((uint)Si[(C2 >> 24) & 255] << 24)) ^ kw[1]; - r2 = Inv_Mcol((uint)Si[C2 & 255] ^ (((uint)Si[(C1 >> 8) & 255]) << 8) ^ (((uint)Si[(C0 >> 16) & 255]) << 16) ^ ((uint)Si[(C3 >> 24) & 255] << 24)) ^ kw[2]; - r3 = Inv_Mcol((uint)Si[C3 & 255] ^ (((uint)Si[(C2 >> 8) & 255]) << 8) ^ (((uint)Si[(C1 >> 16) & 255]) << 16) ^ ((uint)Si[(C0 >> 24) & 255] << 24)) ^ kw[3]; - - // the final round's table is a simple function of Si - - kw = KW[--r]; - C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; - C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; - C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; - C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; - - Debug.Assert(r == 0); + public virtual 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("AES engine not initialised"); + + Check.DataLength(input, inOff, 16, "input buffer too short"); + Check.OutputLength(output, outOff, 16, "output buffer too short"); + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(WorkingKey); + } + else + { + DecryptBlock(WorkingKey); + } + + PackBlock(output, outOff); + + return BLOCK_SIZE; + } + + public virtual void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + C0 = Pack.LE_To_UInt32(bytes, off); + C1 = Pack.LE_To_UInt32(bytes, off + 4); + C2 = Pack.LE_To_UInt32(bytes, off + 8); + C3 = Pack.LE_To_UInt32(bytes, off + 12); + } + + private void PackBlock( + byte[] bytes, + int off) + { + Pack.UInt32_To_LE(C0, bytes, off); + Pack.UInt32_To_LE(C1, bytes, off + 4); + Pack.UInt32_To_LE(C2, bytes, off + 8); + Pack.UInt32_To_LE(C3, bytes, off + 12); + } + + private void EncryptBlock(uint[][] KW) + { + uint[] kw = KW[0]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1]; + r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3]; + kw = KW[r++]; + t0 = Mcol((uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + t1 = Mcol((uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24)) ^ kw[1]; + t2 = Mcol((uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24)) ^ kw[3]; + } + + kw = KW[r++]; + r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1]; + r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3]; + + // the final round is a simple function of S + + kw = KW[r]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + } + + private void DecryptBlock(uint[][] KW) + { + uint[] kw = KW[ROUNDS]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0]; + r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1]; + r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3]; + kw = KW[r--]; + t0 = Inv_Mcol((uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ ((uint)Si[(r1 >> 24) & 255] << 24)) ^ kw[0]; + t1 = Inv_Mcol((uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(r2 >> 24) & 255] << 24)) ^ kw[1]; + t2 = Inv_Mcol((uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ ((uint)Si[(r0 >> 24) & 255] << 24)) ^ kw[3]; + } + + kw = KW[1]; + r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0]; + r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1]; + r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3]; + + // the final round's table is a simple function of Si + + kw = KW[0]; + this.C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; } - } + } } diff --git a/crypto/src/crypto/engines/BlowfishEngine.cs b/crypto/src/crypto/engines/BlowfishEngine.cs
index 8f80f712e..7b50e832f 100644 --- a/crypto/src/crypto/engines/BlowfishEngine.cs +++ b/crypto/src/crypto/engines/BlowfishEngine.cs
@@ -296,7 +296,7 @@ namespace Org.BouncyCastle.Crypto.Engines //==================================== private static readonly int ROUNDS = 16; - private const int BLOCK_SIZE = 8; // bytes = 64 bits + private const int BLOCK_SIZE = 8; // bytes = 64 bits private static readonly int SBOX_SK = 256; private static readonly int P_SZ = ROUNDS+2; @@ -353,19 +353,10 @@ namespace Org.BouncyCastle.Crypto.Engines 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"); - } + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); if (encrypting) { diff --git a/crypto/src/crypto/engines/CamelliaEngine.cs b/crypto/src/crypto/engines/CamelliaEngine.cs
index 8f4a442e9..71bd1b0dc 100644 --- a/crypto/src/crypto/engines/CamelliaEngine.cs +++ b/crypto/src/crypto/engines/CamelliaEngine.cs
@@ -611,7 +611,7 @@ namespace Org.BouncyCastle.Crypto.Engines { } - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -623,22 +623,22 @@ namespace Org.BouncyCastle.Crypto.Engines initialised = true; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Camellia"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -646,12 +646,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + if (_keyIs128) { return processBlock128(input, inOff, output, outOff); } @@ -661,7 +660,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public void Reset() + public virtual void Reset() { // nothing } diff --git a/crypto/src/crypto/engines/CamelliaLightEngine.cs b/crypto/src/crypto/engines/CamelliaLightEngine.cs
index a301eb55e..a132227c5 100644 --- a/crypto/src/crypto/engines/CamelliaLightEngine.cs +++ b/crypto/src/crypto/engines/CamelliaLightEngine.cs
@@ -524,22 +524,22 @@ namespace Org.BouncyCastle.Crypto.Engines initialised = false; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Camellia"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -551,7 +551,7 @@ namespace Org.BouncyCastle.Crypto.Engines initialised = true; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -559,12 +559,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + if (_keyis128) { return processBlock128(input, inOff, output, outOff); } @@ -574,7 +573,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public void Reset() + public virtual void Reset() { } } diff --git a/crypto/src/crypto/engines/Cast5Engine.cs b/crypto/src/crypto/engines/Cast5Engine.cs
index 4c3f84a55..1af30a335 100644 --- a/crypto/src/crypto/engines/Cast5Engine.cs +++ b/crypto/src/crypto/engines/Cast5Engine.cs
@@ -329,7 +329,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -360,12 +360,11 @@ namespace Org.BouncyCastle.Crypto.Engines 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) + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(output, outOff, blockSize, "output buffer too short"); + + if (_encrypting) { return EncryptBlock(input, inOff, output, outOff); } diff --git a/crypto/src/crypto/engines/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs
index f4a7b8fe1..46b59ed2e 100644 --- a/crypto/src/crypto/engines/ChaChaEngine.cs +++ b/crypto/src/crypto/engines/ChaChaEngine.cs
@@ -162,7 +162,6 @@ namespace Org.BouncyCastle.Crypto.Engines x09 += x14; x04 = R(x04 ^ x09, 12); x03 += x04; x14 = R(x14 ^ x03, 8); x09 += x14; x04 = R(x04 ^ x09, 7); - } x[ 0] = x00 + input[ 0]; @@ -182,8 +181,6 @@ namespace Org.BouncyCastle.Crypto.Engines x[14] = x14 + input[14]; x[15] = x15 + input[15]; } - } - } diff --git a/crypto/src/crypto/engines/DesEdeEngine.cs b/crypto/src/crypto/engines/DesEdeEngine.cs
index eec4ec59d..bc40b56a8 100644 --- a/crypto/src/crypto/engines/DesEdeEngine.cs +++ b/crypto/src/crypto/engines/DesEdeEngine.cs
@@ -70,10 +70,9 @@ namespace Org.BouncyCastle.Crypto.Engines { 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"); + + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); byte[] temp = new byte[BLOCK_SIZE]; diff --git a/crypto/src/crypto/engines/DesEdeWrapEngine.cs b/crypto/src/crypto/engines/DesEdeWrapEngine.cs
index fdc71687f..43100a9bd 100644 --- a/crypto/src/crypto/engines/DesEdeWrapEngine.cs +++ b/crypto/src/crypto/engines/DesEdeWrapEngine.cs
@@ -52,7 +52,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param forWrapping * @param param */ - public void Init( + public virtual void Init( bool forWrapping, ICipherParameters parameters) { @@ -103,7 +103,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return */ - public string AlgorithmName + public virtual string AlgorithmName { get { return "DESede"; } } @@ -116,7 +116,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param inLen * @return */ - public byte[] Wrap( + public virtual byte[] Wrap( byte[] input, int inOff, int length) @@ -185,7 +185,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return * @throws InvalidCipherTextException */ - public byte[] Unwrap( + public virtual byte[] Unwrap( byte[] input, int inOff, int length) diff --git a/crypto/src/crypto/engines/DesEngine.cs b/crypto/src/crypto/engines/DesEngine.cs
index 067cf45e3..a6d580bb6 100644 --- a/crypto/src/crypto/engines/DesEngine.cs +++ b/crypto/src/crypto/engines/DesEngine.cs
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Crypto.Engines get { return "DES"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } @@ -59,12 +59,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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); + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + DesFunc(workingKey, input, inOff, output, outOff); return BLOCK_SIZE; } diff --git a/crypto/src/crypto/engines/ElGamalEngine.cs b/crypto/src/crypto/engines/ElGamalEngine.cs
index 3d256a087..197d7bc15 100644 --- a/crypto/src/crypto/engines/ElGamalEngine.cs +++ b/crypto/src/crypto/engines/ElGamalEngine.cs
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Engines private bool forEncryption; private int bitSize; - public string AlgorithmName + public virtual string AlgorithmName { get { return "ElGamal"; } } @@ -28,7 +28,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param forEncryption true if we are encrypting, false otherwise. * @param param the necessary ElGamal key parameters. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an input block. */ - public int GetInputBlockSize() + public virtual int GetInputBlockSize() { if (forEncryption) { @@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an output block. */ - public int GetOutputBlockSize() + public virtual int GetOutputBlockSize() { if (forEncryption) { @@ -107,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return the result of the ElGamal process. * @exception DataLengthException the input block is too large. */ - public byte[] ProcessBlock( + public virtual byte[] ProcessBlock( byte[] input, int inOff, int length) diff --git a/crypto/src/crypto/engines/GOST28147Engine.cs b/crypto/src/crypto/engines/GOST28147Engine.cs
index 8eb6f36b5..e37ddaefd 100644 --- a/crypto/src/crypto/engines/GOST28147Engine.cs +++ b/crypto/src/crypto/engines/GOST28147Engine.cs
@@ -150,7 +150,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param parameters the parameters required to set up the cipher. * @exception ArgumentException if the parameters argument is inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -187,48 +187,39 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Gost28147"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BlockSize; } - public int ProcessBlock( + public virtual 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"); - } + Check.DataLength(input, inOff, BlockSize, "input buffer too short"); + Check.OutputLength(output, outOff, BlockSize, "output buffer too short"); - Gost28147Func(workingKey, input, inOff, output, outOff); + Gost28147Func(workingKey, input, inOff, output, outOff); return BlockSize; } - public void Reset() + public virtual void Reset() { } diff --git a/crypto/src/crypto/engines/HC128Engine.cs b/crypto/src/crypto/engines/HC128Engine.cs
index a2d099f87..40c7a4e17 100644 --- a/crypto/src/crypto/engines/HC128Engine.cs +++ b/crypto/src/crypto/engines/HC128Engine.cs
@@ -142,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Engines cnt = 0; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "HC-128"; } } @@ -156,7 +156,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @throws ArgumentException if the params argument is * inappropriate (ie. the key is not 128 bit long). */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -201,7 +201,7 @@ namespace Org.BouncyCastle.Crypto.Engines return ret; } - public void ProcessBytes( + public virtual void ProcessBytes( byte[] input, int inOff, int len, @@ -210,24 +210,23 @@ namespace Org.BouncyCastle.Crypto.Engines { 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++) + Check.DataLength(input, inOff, len, "input buffer too short"); + Check.OutputLength(output, outOff, len, "output buffer too short"); + + for (int i = 0; i < len; i++) { output[outOff + i] = (byte)(input[inOff + i] ^ GetByte()); } } - public void Reset() + public virtual void Reset() { idx = 0; Init(); } - public byte ReturnByte(byte input) + public virtual byte ReturnByte(byte input) { return (byte)(input ^ GetByte()); } diff --git a/crypto/src/crypto/engines/HC256Engine.cs b/crypto/src/crypto/engines/HC256Engine.cs
index da717dab7..6eb360711 100644 --- a/crypto/src/crypto/engines/HC256Engine.cs +++ b/crypto/src/crypto/engines/HC256Engine.cs
@@ -126,7 +126,7 @@ namespace Org.BouncyCastle.Crypto.Engines cnt = 0; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "HC-256"; } } @@ -140,7 +140,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @throws ArgumentException if the params argument is * inappropriate (ie. the key is not 256 bit long). */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -185,7 +185,7 @@ namespace Org.BouncyCastle.Crypto.Engines return ret; } - public void ProcessBytes( + public virtual void ProcessBytes( byte[] input, int inOff, int len, @@ -194,24 +194,23 @@ namespace Org.BouncyCastle.Crypto.Engines { 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++) + Check.DataLength(input, inOff, len, "input buffer too short"); + Check.OutputLength(output, outOff, len, "output buffer too short"); + + for (int i = 0; i < len; i++) { output[outOff + i] = (byte)(input[inOff + i] ^ GetByte()); } } - public void Reset() + public virtual void Reset() { idx = 0; Init(); } - public byte ReturnByte(byte input) + public virtual byte ReturnByte(byte input) { return (byte)(input ^ GetByte()); } diff --git a/crypto/src/crypto/engines/ISAACEngine.cs b/crypto/src/crypto/engines/ISAACEngine.cs
index 9c58678a0..f25577130 100644 --- a/crypto/src/crypto/engines/ISAACEngine.cs +++ b/crypto/src/crypto/engines/ISAACEngine.cs
@@ -35,7 +35,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the params argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -53,7 +53,7 @@ namespace Org.BouncyCastle.Crypto.Engines setKey(p.GetKey()); } - public byte ReturnByte( + public virtual byte ReturnByte( byte input) { if (index == 0) @@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Crypto.Engines return output; } - public void ProcessBytes( + public virtual void ProcessBytes( byte[] input, int inOff, int len, @@ -77,10 +77,9 @@ namespace Org.BouncyCastle.Crypto.Engines { 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"); + + Check.DataLength(input, inOff, len, "input buffer too short"); + Check.OutputLength(output, outOff, len, "output buffer too short"); for (int i = 0; i < len; i++) { @@ -94,12 +93,12 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public string AlgorithmName + public virtual string AlgorithmName { get { return "ISAAC"; } } - public void Reset() + public virtual void Reset() { setKey(workingKey); } diff --git a/crypto/src/crypto/engines/IdeaEngine.cs b/crypto/src/crypto/engines/IdeaEngine.cs
index 46b5a787c..4909510ac 100644 --- a/crypto/src/crypto/engines/IdeaEngine.cs +++ b/crypto/src/crypto/engines/IdeaEngine.cs
@@ -47,7 +47,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -58,43 +58,37 @@ namespace Org.BouncyCastle.Crypto.Engines ((KeyParameter)parameters).GetKey()); } - public string AlgorithmName + public virtual string AlgorithmName { get { return "IDEA"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } - public int ProcessBlock( + public virtual 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"); - } + + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + IdeaFunc(workingKey, input, inOff, output, outOff); return BLOCK_SIZE; } - public void Reset() + public virtual void Reset() { } private static readonly int MASK = 0xffff; diff --git a/crypto/src/crypto/engines/IesEngine.cs b/crypto/src/crypto/engines/IesEngine.cs
index 6520c86f8..a2004a9d6 100644 --- a/crypto/src/crypto/engines/IesEngine.cs +++ b/crypto/src/crypto/engines/IesEngine.cs
@@ -72,7 +72,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param pubParam the recipient's/sender's public key parameters * @param param encoding and derivation parameters. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters privParameters, ICipherParameters pubParameters, @@ -133,7 +133,7 @@ namespace Org.BouncyCastle.Crypto.Engines inOff += inLen; - byte[] T1 = Arrays.Copy(in_enc, inOff, macBuf.Length); + byte[] T1 = Arrays.CopyOfRange(in_enc, inOff, inOff + macBuf.Length); if (!Arrays.ConstantTimeAreEqual(T1, macBuf)) throw (new InvalidCipherTextException("Invalid MAC.")); @@ -213,7 +213,7 @@ namespace Org.BouncyCastle.Crypto.Engines return buf; } - public byte[] ProcessBlock( + public virtual byte[] ProcessBlock( byte[] input, int inOff, int inLen) @@ -224,10 +224,16 @@ namespace Org.BouncyCastle.Crypto.Engines byte[] zBytes = BigIntegers.AsUnsignedByteArray(agree.GetFieldSize(), z); - return forEncryption - ? EncryptBlock(input, inOff, inLen, zBytes) - : DecryptBlock(input, inOff, inLen, zBytes); + try + { + return forEncryption + ? EncryptBlock(input, inOff, inLen, zBytes) + : DecryptBlock(input, inOff, inLen, zBytes); + } + finally + { + Array.Clear(zBytes, 0, zBytes.Length); + } } } - } diff --git a/crypto/src/crypto/engines/NaccacheSternEngine.cs b/crypto/src/crypto/engines/NaccacheSternEngine.cs
index 9ca092351..e547e0caf 100644 --- a/crypto/src/crypto/engines/NaccacheSternEngine.cs +++ b/crypto/src/crypto/engines/NaccacheSternEngine.cs
@@ -33,7 +33,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @see org.bouncycastle.crypto.AsymmetricBlockCipher#init(bool, * org.bouncycastle.crypto.CipherParameters) */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public bool Debug + public virtual bool Debug { set { this.debug = value; } } @@ -93,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetInputBlockSize() */ - public int GetInputBlockSize() + public virtual int GetInputBlockSize() { if (forEncryption) { @@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetOutputBlockSize() */ - public int GetOutputBlockSize() + public virtual int GetOutputBlockSize() { if (forEncryption) { @@ -134,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @see org.bouncycastle.crypto.AsymmetricBlockCipher#ProcessBlock(byte[], * int, int) */ - public byte[] ProcessBlock( + public virtual byte[] ProcessBlock( byte[] inBytes, int inOff, int length) @@ -245,7 +245,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return The byte[] representation of the encrypted BigInteger (i.e. * crypted.toByteArray()) */ - public byte[] Encrypt( + public virtual byte[] Encrypt( BigInteger plain) { // Always return modulus size values 0-padded at the beginning @@ -273,7 +273,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return encrypt((block1 + block2) mod sigma) * @throws InvalidCipherTextException */ - public byte[] AddCryptedBlocks( + public virtual byte[] AddCryptedBlocks( byte[] block1, byte[] block2) { @@ -329,7 +329,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return the data after it went through the NaccacheSternEngine. * @throws InvalidCipherTextException */ - public byte[] ProcessData( + public virtual byte[] ProcessData( byte[] data) { if (debug) diff --git a/crypto/src/crypto/engines/NoekeonEngine.cs b/crypto/src/crypto/engines/NoekeonEngine.cs
index b73e696a9..dd78a4ea5 100644 --- a/crypto/src/crypto/engines/NoekeonEngine.cs +++ b/crypto/src/crypto/engines/NoekeonEngine.cs
@@ -42,17 +42,17 @@ namespace Org.BouncyCastle.Crypto.Engines _initialised = false; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Noekeon"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return GenericSize; } @@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the params argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -80,7 +80,7 @@ namespace Org.BouncyCastle.Crypto.Engines setKey(p.GetKey()); } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -88,17 +88,16 @@ namespace Org.BouncyCastle.Crypto.Engines { 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 + Check.DataLength(input, inOff, GenericSize, "input buffer too short"); + Check.OutputLength(output, outOff, GenericSize, "output buffer too short"); + + return _forEncryption ? encryptBlock(input, inOff, output, outOff) : decryptBlock(input, inOff, output, outOff); } - public void Reset() + public virtual void Reset() { // TODO This should do something in case the encryption is aborted } diff --git a/crypto/src/crypto/engines/NullEngine.cs b/crypto/src/crypto/engines/NullEngine.cs
index 407b8ccc6..f883b7c29 100644 --- a/crypto/src/crypto/engines/NullEngine.cs +++ b/crypto/src/crypto/engines/NullEngine.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Crypto.Engines { } - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -26,22 +26,22 @@ namespace Org.BouncyCastle.Crypto.Engines initialised = true; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Null"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return true; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BlockSize; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -49,12 +49,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BlockSize, "input buffer too short"); + Check.OutputLength(output, outOff, BlockSize, "output buffer too short"); + + for (int i = 0; i < BlockSize; ++i) { output[outOff + i] = input[inOff + i]; } @@ -62,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } - public void Reset() + public virtual void Reset() { // nothing needs to be done } diff --git a/crypto/src/crypto/engines/RC2Engine.cs b/crypto/src/crypto/engines/RC2Engine.cs
index aaf8c714c..b56953de5 100644 --- a/crypto/src/crypto/engines/RC2Engine.cs +++ b/crypto/src/crypto/engines/RC2Engine.cs
@@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -139,26 +139,26 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public void Reset() + public virtual void Reset() { } - public string AlgorithmName + public virtual string AlgorithmName { get { return "RC2"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -166,12 +166,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) { EncryptBlock(input, inOff, output, outOff); } @@ -308,5 +307,4 @@ namespace Org.BouncyCastle.Crypto.Engines outBytes[outOff + 7] = (byte)(x76 >> 8); } } - } diff --git a/crypto/src/crypto/engines/RC2WrapEngine.cs b/crypto/src/crypto/engines/RC2WrapEngine.cs
index 238c9f76a..5742aa8b7 100644 --- a/crypto/src/crypto/engines/RC2WrapEngine.cs +++ b/crypto/src/crypto/engines/RC2WrapEngine.cs
@@ -51,7 +51,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param forWrapping * @param param */ - public void Init( + public virtual void Init( bool forWrapping, ICipherParameters parameters) { @@ -101,7 +101,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return */ - public string AlgorithmName + public virtual string AlgorithmName { get { return "RC2"; } } @@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param inLen * @return */ - public byte[] Wrap( + public virtual byte[] Wrap( byte[] input, int inOff, int length) @@ -215,7 +215,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return * @throws InvalidCipherTextException */ - public byte[] Unwrap( + public virtual byte[] Unwrap( byte[] input, int inOff, int length) diff --git a/crypto/src/crypto/engines/RC4Engine.cs b/crypto/src/crypto/engines/RC4Engine.cs
index c65468d93..fd84b7d23 100644 --- a/crypto/src/crypto/engines/RC4Engine.cs +++ b/crypto/src/crypto/engines/RC4Engine.cs
@@ -27,7 +27,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -47,12 +47,12 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException("invalid parameter passed to RC4 init - " + parameters.GetType().ToString()); } - public string AlgorithmName + public virtual string AlgorithmName { get { return "RC4"; } } - public byte ReturnByte( + public virtual byte ReturnByte( byte input) { x = (x + 1) & 0xff; @@ -67,23 +67,15 @@ namespace Org.BouncyCastle.Crypto.Engines return (byte)(input ^ engineState[(engineState[x] + engineState[y]) & 0xff]); } - public void ProcessBytes( + public virtual void ProcessBytes( byte[] input, int inOff, int length, byte[] output, - int outOff - ) + 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"); - } + Check.DataLength(input, inOff, length, "input buffer too short"); + Check.OutputLength(output, outOff, length, "output buffer too short"); for (int i = 0; i < length ; i++) { @@ -101,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public void Reset() + public virtual void Reset() { SetKey(workingKey); } @@ -143,5 +135,4 @@ namespace Org.BouncyCastle.Crypto.Engines } } } - } diff --git a/crypto/src/crypto/engines/RC532Engine.cs b/crypto/src/crypto/engines/RC532Engine.cs
index 1661707ef..169a60b98 100644 --- a/crypto/src/crypto/engines/RC532Engine.cs +++ b/crypto/src/crypto/engines/RC532Engine.cs
@@ -48,17 +48,17 @@ namespace Org.BouncyCastle.Crypto.Engines // _S = null; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "RC5-32"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return 2 * 4; } @@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Crypto.Engines this.forEncryption = forEncryption; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Crypto.Engines : DecryptBlock(input, inOff, output, outOff); } - public void Reset() + public virtual void Reset() { } @@ -290,5 +290,4 @@ namespace Org.BouncyCastle.Crypto.Engines dst[dstOff + 3] = (byte)(word >> 24); } } - } diff --git a/crypto/src/crypto/engines/RC564Engine.cs b/crypto/src/crypto/engines/RC564Engine.cs
index 5c69d40ff..ddcce0fa8 100644 --- a/crypto/src/crypto/engines/RC564Engine.cs +++ b/crypto/src/crypto/engines/RC564Engine.cs
@@ -51,17 +51,17 @@ namespace Org.BouncyCastle.Crypto.Engines // _S = null; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "RC5-64"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return 2 * bytesPerWord; } @@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Engines SetKey(p.GetKey()); } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -102,7 +102,7 @@ namespace Org.BouncyCastle.Crypto.Engines : DecryptBlock(input, inOff, output, outOff); } - public void Reset() + public virtual void Reset() { } @@ -291,5 +291,4 @@ namespace Org.BouncyCastle.Crypto.Engines } } } - } diff --git a/crypto/src/crypto/engines/RC6Engine.cs b/crypto/src/crypto/engines/RC6Engine.cs
index d72cc2f7b..196bd8394 100644 --- a/crypto/src/crypto/engines/RC6Engine.cs +++ b/crypto/src/crypto/engines/RC6Engine.cs
@@ -48,17 +48,17 @@ namespace Org.BouncyCastle.Crypto.Engines // _S = null; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "RC6"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return 4 * bytesPerWord; } @@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -84,7 +84,7 @@ namespace Org.BouncyCastle.Crypto.Engines SetKey(p.GetKey()); } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -93,17 +93,16 @@ namespace Org.BouncyCastle.Crypto.Engines 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) + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(output, outOff, blockSize, "output buffer too short"); + + return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff); } - public void Reset() + public virtual void Reset() { } @@ -358,5 +357,4 @@ namespace Org.BouncyCastle.Crypto.Engines } } } - } diff --git a/crypto/src/crypto/engines/RFC3211WrapEngine.cs b/crypto/src/crypto/engines/RFC3211WrapEngine.cs
index e520075f9..4e3af5227 100644 --- a/crypto/src/crypto/engines/RFC3211WrapEngine.cs +++ b/crypto/src/crypto/engines/RFC3211WrapEngine.cs
@@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Engines this.engine = new CbcBlockCipher(engine); } - public void Init( + public virtual void Init( bool forWrapping, ICipherParameters param) { @@ -48,12 +48,12 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public string AlgorithmName + public virtual string AlgorithmName { get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; } } - public byte[] Wrap( + public virtual byte[] Wrap( byte[] inBytes, int inOff, int inLen) @@ -99,7 +99,7 @@ namespace Org.BouncyCastle.Crypto.Engines return cekBlock; } - public byte[] Unwrap( + public virtual byte[] Unwrap( byte[] inBytes, int inOff, int inLen) diff --git a/crypto/src/crypto/engines/RFC3394WrapEngine.cs b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
index 5615a63e5..4bb0e2114 100644 --- a/crypto/src/crypto/engines/RFC3394WrapEngine.cs +++ b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
@@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Engines this.engine = engine; } - public void Init( + public virtual void Init( bool forWrapping, ICipherParameters parameters) { @@ -64,12 +64,12 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public string AlgorithmName + public virtual string AlgorithmName { get { return engine.AlgorithmName; } } - public byte[] Wrap( + public virtual byte[] Wrap( byte[] input, int inOff, int inLen) @@ -119,7 +119,7 @@ namespace Org.BouncyCastle.Crypto.Engines return block; } - public byte[] Unwrap( + public virtual byte[] Unwrap( byte[] input, int inOff, int inLen) diff --git a/crypto/src/crypto/engines/RSABlindedEngine.cs b/crypto/src/crypto/engines/RSABlindedEngine.cs
index cdf69ddda..f95f145f6 100644 --- a/crypto/src/crypto/engines/RSABlindedEngine.cs +++ b/crypto/src/crypto/engines/RSABlindedEngine.cs
@@ -7,118 +7,122 @@ using Org.BouncyCastle.Utilities; 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; + /** + * 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"; } - } + public virtual 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); + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public virtual void Init( + bool forEncryption, + ICipherParameters param) + { + core.Init(forEncryption, param); - if (param is ParametersWithRandom) - { - ParametersWithRandom rParam = (ParametersWithRandom)param; + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; - key = (RsaKeyParameters)rParam.Parameters; - random = rParam.Random; - } - else - { - key = (RsaKeyParameters)param; - random = new SecureRandom(); - } - } + 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 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 virtual 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(); - } + /** + * 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 virtual 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"); + /** + * 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 virtual 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 input = core.ConvertInput(inBuf, inOff, inLen); - BigInteger result; - if (key is RsaPrivateCrtKeyParameters) - { - RsaPrivateCrtKeyParameters k = (RsaPrivateCrtKeyParameters)key; - BigInteger e = k.PublicExponent; - if (e != null) // can't do blinding without a public exponent - { - BigInteger m = k.Modulus; - BigInteger r = BigIntegers.CreateRandomInRange( - BigInteger.One, m.Subtract(BigInteger.One), random); + BigInteger result; + if (key is RsaPrivateCrtKeyParameters) + { + RsaPrivateCrtKeyParameters k = (RsaPrivateCrtKeyParameters)key; + BigInteger e = k.PublicExponent; + if (e != null) // can't do blinding without a public exponent + { + BigInteger m = k.Modulus; + BigInteger r = BigIntegers.CreateRandomInRange( + BigInteger.One, m.Subtract(BigInteger.One), random); - BigInteger blindedInput = r.ModPow(e, m).Multiply(input).Mod(m); - BigInteger blindedResult = core.ProcessBlock(blindedInput); + BigInteger blindedInput = r.ModPow(e, m).Multiply(input).Mod(m); + BigInteger blindedResult = core.ProcessBlock(blindedInput); - BigInteger rInv = r.ModInverse(m); - result = blindedResult.Multiply(rInv).Mod(m); - } - else - { - result = core.ProcessBlock(input); - } - } - else - { - result = core.ProcessBlock(input); - } + BigInteger rInv = r.ModInverse(m); + result = blindedResult.Multiply(rInv).Mod(m); - return core.ConvertOutput(result); - } - } + // defence against Arjen Lenstra’s CRT attack + if (!input.Equals(result.ModPow(e, m))) + throw new InvalidOperationException("RSA engine faulty decryption/signing detected"); + } + else + { + result = core.ProcessBlock(input); + } + } + else + { + result = core.ProcessBlock(input); + } + + return core.ConvertOutput(result); + } + } } diff --git a/crypto/src/crypto/engines/RSABlindingEngine.cs b/crypto/src/crypto/engines/RSABlindingEngine.cs
index 76b57a3f7..c636627bf 100644 --- a/crypto/src/crypto/engines/RSABlindingEngine.cs +++ b/crypto/src/crypto/engines/RSABlindingEngine.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Engines private bool forEncryption; - public string AlgorithmName + public virtual string AlgorithmName { get { return "RSA"; } } @@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param forEncryption true if we are encrypting (blinding), false otherwise. * @param param the necessary RSA key parameters. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters param) { @@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an input block. */ - public int GetInputBlockSize() + public virtual int GetInputBlockSize() { return core.GetInputBlockSize(); } @@ -75,7 +75,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an output block. */ - public int GetOutputBlockSize() + public virtual int GetOutputBlockSize() { return core.GetOutputBlockSize(); } @@ -89,7 +89,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return the result of the RSA process. * @throws DataLengthException the input block is too large. */ - public byte[] ProcessBlock( + public virtual byte[] ProcessBlock( byte[] inBuf, int inOff, int inLen) diff --git a/crypto/src/crypto/engines/RSACoreEngine.cs b/crypto/src/crypto/engines/RSACoreEngine.cs
index 4e64d25d6..38326371f 100644 --- a/crypto/src/crypto/engines/RSACoreEngine.cs +++ b/crypto/src/crypto/engines/RSACoreEngine.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param forEncryption true if we are encrypting, false otherwise. * @param param the necessary RSA key parameters. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -45,7 +45,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an input block. */ - public int GetInputBlockSize() + public virtual int GetInputBlockSize() { if (forEncryption) { @@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an output block. */ - public int GetOutputBlockSize() + public virtual int GetOutputBlockSize() { if (forEncryption) { @@ -72,7 +72,7 @@ namespace Org.BouncyCastle.Crypto.Engines return (bitSize - 1) / 8; } - public BigInteger ConvertInput( + public virtual BigInteger ConvertInput( byte[] inBuf, int inOff, int inLen) @@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Crypto.Engines return input; } - public byte[] ConvertOutput( + public virtual byte[] ConvertOutput( BigInteger result) { byte[] output = result.ToByteArrayUnsigned(); @@ -112,7 +112,7 @@ namespace Org.BouncyCastle.Crypto.Engines return output; } - public BigInteger ProcessBlock( + public virtual BigInteger ProcessBlock( BigInteger input) { if (key is RsaPrivateCrtKeyParameters) diff --git a/crypto/src/crypto/engines/RijndaelEngine.cs b/crypto/src/crypto/engines/RijndaelEngine.cs
index df2e5baea..80f522353 100644 --- a/crypto/src/crypto/engines/RijndaelEngine.cs +++ b/crypto/src/crypto/engines/RijndaelEngine.cs
@@ -571,7 +571,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -585,43 +585,34 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException("invalid parameter passed to Rijndael init - " + parameters.GetType().ToString()); } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Rijndael"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BC / 2; } - public int ProcessBlock( + public virtual 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"); - } + Check.DataLength(input, inOff, (BC / 2), "input buffer too short"); + Check.OutputLength(output, outOff, (BC / 2), "output buffer too short"); - UnPackBlock(input, inOff); + UnPackBlock(input, inOff); if (forEncryption) { @@ -637,11 +628,11 @@ namespace Org.BouncyCastle.Crypto.Engines return BC / 2; } - public void Reset() + public virtual void Reset() { } - private void UnPackBlock( + private void UnPackBlock( byte[] bytes, int off) { @@ -743,5 +734,4 @@ namespace Org.BouncyCastle.Crypto.Engines KeyAddition(rk[0]); } } - } diff --git a/crypto/src/crypto/engines/RsaEngine.cs b/crypto/src/crypto/engines/RsaEngine.cs
index 7e6dfb163..4399b4409 100644 --- a/crypto/src/crypto/engines/RsaEngine.cs +++ b/crypto/src/crypto/engines/RsaEngine.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Engines { private RsaCoreEngine core; - public string AlgorithmName + public virtual string AlgorithmName { get { return "RSA"; } } @@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @param forEncryption true if we are encrypting, false otherwise. * @param param the necessary RSA key parameters. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -38,7 +38,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an input block. */ - public int GetInputBlockSize() + public virtual int GetInputBlockSize() { return core.GetInputBlockSize(); } @@ -50,7 +50,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @return maximum size for an output block. */ - public int GetOutputBlockSize() + public virtual int GetOutputBlockSize() { return core.GetOutputBlockSize(); } @@ -64,7 +64,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @return the result of the RSA process. * @exception DataLengthException the input block is too large. */ - public byte[] ProcessBlock( + public virtual byte[] ProcessBlock( byte[] inBuf, int inOff, int inLen) diff --git a/crypto/src/crypto/engines/SEEDEngine.cs b/crypto/src/crypto/engines/SEEDEngine.cs
index efea0f1fe..f615b8476 100644 --- a/crypto/src/crypto/engines/SEEDEngine.cs +++ b/crypto/src/crypto/engines/SEEDEngine.cs
@@ -168,7 +168,7 @@ namespace Org.BouncyCastle.Crypto.Engines private int[] wKey; private bool forEncryption; - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -176,22 +176,22 @@ namespace Org.BouncyCastle.Crypto.Engines wKey = createWorkingKey(((KeyParameter)parameters).GetKey()); } - public string AlgorithmName + public virtual string AlgorithmName { get { return "SEED"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BlockSize; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] inBuf, int inOff, byte[] outBuf, @@ -199,12 +199,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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); + Check.DataLength(inBuf, inOff, BlockSize, "input buffer too short"); + Check.OutputLength(outBuf, outOff, BlockSize, "output buffer too short"); + + long l = bytesToLong(inBuf, inOff + 0); long r = bytesToLong(inBuf, inOff + 8); if (forEncryption) @@ -234,7 +233,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } - public void Reset() + public virtual void Reset() { } diff --git a/crypto/src/crypto/engines/Salsa20Engine.cs b/crypto/src/crypto/engines/Salsa20Engine.cs
index 81884d603..9b27dc7b4 100644 --- a/crypto/src/crypto/engines/Salsa20Engine.cs +++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -61,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Engines this.rounds = rounds; } - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public byte ReturnByte( + public virtual byte ReturnByte( byte input) { if (LimitExceeded()) @@ -136,7 +136,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public void ProcessBytes( + public virtual void ProcessBytes( byte[] inBytes, int inOff, int len, @@ -144,26 +144,15 @@ namespace Org.BouncyCastle.Crypto.Engines int outOff) { if (!initialised) - { throw new InvalidOperationException(AlgorithmName + " not initialised"); - } - if ((inOff + len) > inBytes.Length) - { - throw new DataLengthException("input buffer too short"); - } + Check.DataLength(inBytes, inOff, len, "input buffer too short"); + Check.OutputLength(outBytes, outOff, len, "output buffer too short"); - if ((outOff + len) > outBytes.Length) - { - throw new DataLengthException("output buffer too short"); - } - - if (LimitExceeded((uint)len)) - { + if (LimitExceeded((uint)len)) throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV"); - } - for (int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { if (index == 0) { @@ -175,7 +164,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public void Reset() + public virtual void Reset() { index = 0; ResetLimitCounter(); diff --git a/crypto/src/crypto/engines/SerpentEngine.cs b/crypto/src/crypto/engines/SerpentEngine.cs
index 92b25acc6..255c204ab 100644 --- a/crypto/src/crypto/engines/SerpentEngine.cs +++ b/crypto/src/crypto/engines/SerpentEngine.cs
@@ -37,7 +37,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -48,17 +48,17 @@ namespace Org.BouncyCastle.Crypto.Engines this.wKey = MakeWorkingKey(((KeyParameter)parameters).GetKey()); } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Serpent"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } @@ -76,7 +76,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception InvalidOperationException if the cipher isn't initialised. * @return the number of bytes processed and produced. */ - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -84,12 +84,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) { EncryptBlock(input, inOff, output, outOff); } @@ -101,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public void Reset() + public virtual void Reset() { } @@ -775,5 +774,4 @@ namespace Org.BouncyCastle.Crypto.Engines X0 = RotateRight(x0, 13); } } - } diff --git a/crypto/src/crypto/engines/SkipjackEngine.cs b/crypto/src/crypto/engines/SkipjackEngine.cs
index 3d2a781e6..a45dc9b24 100644 --- a/crypto/src/crypto/engines/SkipjackEngine.cs +++ b/crypto/src/crypto/engines/SkipjackEngine.cs
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -71,22 +71,22 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public string AlgorithmName + public virtual string AlgorithmName { get { return "SKIPJACK"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return BLOCK_SIZE; } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -94,12 +94,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) { EncryptBlock(input, inOff, output, outOff); } @@ -111,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public void Reset() + public virtual void Reset() { } @@ -135,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Engines return ((g5 << 8) + g6); } - public int EncryptBlock( + public virtual int EncryptBlock( byte[] input, int inOff, byte[] outBytes, @@ -203,7 +202,7 @@ namespace Org.BouncyCastle.Crypto.Engines return ((h6 << 8) + h5); } - public int DecryptBlock( + public virtual int DecryptBlock( byte[] input, int inOff, byte[] outBytes, @@ -251,5 +250,4 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } } - } diff --git a/crypto/src/crypto/engines/TEAEngine.cs b/crypto/src/crypto/engines/TEAEngine.cs
index 582dd0f73..2e1a7002b 100644 --- a/crypto/src/crypto/engines/TEAEngine.cs +++ b/crypto/src/crypto/engines/TEAEngine.cs
@@ -36,17 +36,17 @@ namespace Org.BouncyCastle.Crypto.Engines _initialised = false; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "TEA"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return block_size; } @@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the params argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -77,7 +77,7 @@ namespace Org.BouncyCastle.Crypto.Engines setKey(p.GetKey()); } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] inBytes, int inOff, byte[] outBytes, @@ -86,18 +86,15 @@ namespace Org.BouncyCastle.Crypto.Engines if (!_initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); - if ((inOff + block_size) > inBytes.Length) - throw new DataLengthException("input buffer too short"); + Check.DataLength(inBytes, inOff, block_size, "input buffer too short"); + Check.OutputLength(outBytes, outOff, block_size, "output buffer too short"); - if ((outOff + block_size) > outBytes.Length) - throw new DataLengthException("output buffer too short"); - - return _forEncryption + return _forEncryption ? encryptBlock(inBytes, inOff, outBytes, outOff) : decryptBlock(inBytes, inOff, outBytes, outOff); } - public void Reset() + public virtual void Reset() { } diff --git a/crypto/src/crypto/engines/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs
index 954470345..33ff3a421 100644 --- a/crypto/src/crypto/engines/ThreefishEngine.cs +++ b/crypto/src/crypto/engines/ThreefishEngine.cs
@@ -155,7 +155,7 @@ namespace Org.BouncyCastle.Crypto.Engines /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param> /// <param name="parameters">an instance of <see cref="TweakableBlockCipherParameters"/> or <see cref="KeyParameter"/> (to /// use a 0 tweak)</param> - public void Init(bool forEncryption, ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { byte[] keyBytes; byte[] tweakBytes; @@ -266,26 +266,26 @@ namespace Org.BouncyCastle.Crypto.Engines t[4] = t[1]; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "Threefish-" + (blocksizeBytes * 8); } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return blocksizeBytes; } - public void Reset() + public virtual void Reset() { } - public int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { if ((outOff + blocksizeBytes) > outBytes.Length) { diff --git a/crypto/src/crypto/engines/TwofishEngine.cs b/crypto/src/crypto/engines/TwofishEngine.cs
index b983d9d31..04a579ced 100644 --- a/crypto/src/crypto/engines/TwofishEngine.cs +++ b/crypto/src/crypto/engines/TwofishEngine.cs
@@ -293,12 +293,11 @@ namespace Org.BouncyCastle.Crypto.Engines { 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) + Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) { EncryptBlock(input, inOff, output, outOff); } diff --git a/crypto/src/crypto/engines/VMPCEngine.cs b/crypto/src/crypto/engines/VMPCEngine.cs
index 1c2802a80..852901e36 100644 --- a/crypto/src/crypto/engines/VMPCEngine.cs +++ b/crypto/src/crypto/engines/VMPCEngine.cs
@@ -92,15 +92,8 @@ namespace Org.BouncyCastle.Crypto.Engines 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"); - } + Check.DataLength(input, inOff, len, "input buffer too short"); + Check.OutputLength(output, outOff, len, "output buffer too short"); for (int i = 0; i < len; i++) { diff --git a/crypto/src/crypto/engines/XSalsa20Engine.cs b/crypto/src/crypto/engines/XSalsa20Engine.cs
index fc6630905..2898b46c8 100644 --- a/crypto/src/crypto/engines/XSalsa20Engine.cs +++ b/crypto/src/crypto/engines/XSalsa20Engine.cs
@@ -13,7 +13,6 @@ namespace Org.BouncyCastle.Crypto.Engines public class XSalsa20Engine : Salsa20Engine { - public override string AlgorithmName { get { return "XSalsa20"; } @@ -65,7 +64,6 @@ namespace Org.BouncyCastle.Crypto.Engines // Counter reset ResetCounter(); } - } } diff --git a/crypto/src/crypto/engines/XTEAEngine.cs b/crypto/src/crypto/engines/XTEAEngine.cs
index eb9291775..40d81fbe6 100644 --- a/crypto/src/crypto/engines/XTEAEngine.cs +++ b/crypto/src/crypto/engines/XTEAEngine.cs
@@ -34,17 +34,17 @@ namespace Org.BouncyCastle.Crypto.Engines _initialised = false; } - public string AlgorithmName + public virtual string AlgorithmName { get { return "XTEA"; } } - public bool IsPartialBlockOkay + public virtual bool IsPartialBlockOkay { get { return false; } } - public int GetBlockSize() + public virtual int GetBlockSize() { return block_size; } @@ -57,7 +57,7 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the params argument is * inappropriate. */ - public void Init( + public virtual void Init( bool forEncryption, ICipherParameters parameters) { @@ -75,7 +75,7 @@ namespace Org.BouncyCastle.Crypto.Engines setKey(p.GetKey()); } - public int ProcessBlock( + public virtual int ProcessBlock( byte[] inBytes, int inOff, byte[] outBytes, @@ -84,18 +84,15 @@ namespace Org.BouncyCastle.Crypto.Engines if (!_initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); - if ((inOff + block_size) > inBytes.Length) - throw new DataLengthException("input buffer too short"); + Check.DataLength(inBytes, inOff, block_size, "input buffer too short"); + Check.OutputLength(outBytes, outOff, block_size, "output buffer too short"); - if ((outOff + block_size) > outBytes.Length) - throw new DataLengthException("output buffer too short"); - - return _forEncryption + return _forEncryption ? encryptBlock(inBytes, inOff, outBytes, outOff) : decryptBlock(inBytes, inOff, outBytes, outOff); } - public void Reset() + public virtual void Reset() { } diff --git a/crypto/src/crypto/generators/DHKeyGeneratorHelper.cs b/crypto/src/crypto/generators/DHKeyGeneratorHelper.cs
index d05c51e80..68aba64f7 100644 --- a/crypto/src/crypto/generators/DHKeyGeneratorHelper.cs +++ b/crypto/src/crypto/generators/DHKeyGeneratorHelper.cs
@@ -2,6 +2,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; @@ -23,7 +24,15 @@ namespace Org.BouncyCastle.Crypto.Generators if (limit != 0) { - return new BigInteger(limit, random).SetBit(limit - 1); + int minWeight = limit >> 2; + for (;;) + { + BigInteger x = new BigInteger(limit, random).SetBit(limit - 1); + if (WNafUtilities.GetNafWeight(x) >= minWeight) + { + return x; + } + } } BigInteger min = BigInteger.Two; @@ -40,7 +49,17 @@ namespace Org.BouncyCastle.Crypto.Generators } BigInteger max = q.Subtract(BigInteger.Two); - return BigIntegers.CreateRandomInRange(min, max, random); + { + int minWeight = max.BitLength >> 2; + for (;;) + { + BigInteger x = BigIntegers.CreateRandomInRange(min, max, random); + if (WNafUtilities.GetNafWeight(x) >= minWeight) + { + return x; + } + } + } } internal BigInteger CalculatePublic( diff --git a/crypto/src/crypto/generators/DsaKeyPairGenerator.cs b/crypto/src/crypto/generators/DsaKeyPairGenerator.cs
index bb8ec591b..1c9ce5a16 100644 --- a/crypto/src/crypto/generators/DsaKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/DsaKeyPairGenerator.cs
@@ -1,8 +1,10 @@ using System; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Security; + using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Generators @@ -11,51 +13,60 @@ namespace Org.BouncyCastle.Crypto.Generators * a DSA key pair generator. * * This Generates DSA keys in line with the method described - * in <i>FIPS 186-3 B.1 FFC Key Pair Generation</i>. + * in <i>FIPS 186-3 B.1 FFC Key Pair Generation</i>. */ public class DsaKeyPairGenerator - : IAsymmetricCipherKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator { + private static readonly BigInteger One = BigInteger.One; + private DsaKeyGenerationParameters param; - public void Init( - KeyGenerationParameters parameters) + public void Init( + KeyGenerationParameters parameters) { - if (parameters == null) - throw new ArgumentNullException("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) + // Note: If we start accepting instances of KeyGenerationParameters, + // must apply constraint checking on strength (see DsaParametersGenerator.Init) - this.param = (DsaKeyGenerationParameters) parameters; + this.param = (DsaKeyGenerationParameters) parameters; } - public AsymmetricCipherKeyPair GenerateKeyPair() + public AsymmetricCipherKeyPair GenerateKeyPair() { - DsaParameters dsaParams = param.Parameters; + DsaParameters dsaParams = param.Parameters; - BigInteger x = GeneratePrivateKey(dsaParams.Q, param.Random); - BigInteger y = CalculatePublicKey(dsaParams.P, dsaParams.G, x); + BigInteger x = GeneratePrivateKey(dsaParams.Q, param.Random); + BigInteger y = CalculatePublicKey(dsaParams.P, dsaParams.G, x); - return new AsymmetricCipherKeyPair( - new DsaPublicKeyParameters(y, dsaParams), - new DsaPrivateKeyParameters(x, dsaParams)); + return new AsymmetricCipherKeyPair( + new DsaPublicKeyParameters(y, dsaParams), + new DsaPrivateKeyParameters(x, dsaParams)); } - private static BigInteger GeneratePrivateKey(BigInteger q, SecureRandom random) - { - // TODO Prefer this method? (change test cases that used fixed random) - // B.1.1 Key Pair Generation Using Extra Random Bits -// BigInteger c = new BigInteger(q.BitLength + 64, random); -// return c.Mod(q.Subtract(BigInteger.One)).Add(BigInteger.One); - - // B.1.2 Key Pair Generation by Testing Candidates - return BigIntegers.CreateRandomInRange(BigInteger.One, q.Subtract(BigInteger.One), random); - } - - private static BigInteger CalculatePublicKey(BigInteger p, BigInteger g, BigInteger x) - { - return g.ModPow(x, p); - } - } + private static BigInteger GeneratePrivateKey(BigInteger q, SecureRandom random) + { + // B.1.2 Key Pair Generation by Testing Candidates + int minWeight = q.BitLength >> 2; + for (;;) + { + // TODO Prefer this method? (change test cases that used fixed random) + // B.1.1 Key Pair Generation Using Extra Random Bits + //BigInteger x = new BigInteger(q.BitLength + 64, random).Mod(q.Subtract(One)).Add(One); + + BigInteger x = BigIntegers.CreateRandomInRange(One, q.Subtract(One), random); + if (WNafUtilities.GetNafWeight(x) >= minWeight) + { + return x; + } + } + } + + private static BigInteger CalculatePublicKey(BigInteger p, BigInteger g, BigInteger x) + { + return g.ModPow(x, p); + } + } } diff --git a/crypto/src/crypto/generators/DsaParametersGenerator.cs b/crypto/src/crypto/generators/DsaParametersGenerator.cs
index cf6343a16..d7ae3ec54 100644 --- a/crypto/src/crypto/generators/DsaParametersGenerator.cs +++ b/crypto/src/crypto/generators/DsaParametersGenerator.cs
@@ -31,13 +31,11 @@ namespace Org.BouncyCastle.Crypto.Generators this.digest = digest; } - /** - * 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. - */ + /// <summary>Initialise the generator</summary> + /// <remarks>This form can only be used for older DSA (pre-DSA2) parameters</remarks> + /// <param name="size">the size of keys in bits (from 512 up to 1024, and a multiple of 64)</param> + /// <param name="certainty">measure of robustness of primes (at least 80 for FIPS 186-2 compliance)</param> + /// <param name="random">the source of randomness to use</param> public virtual void Init( int size, int certainty, @@ -53,14 +51,9 @@ namespace Org.BouncyCastle.Crypto.Generators this.random = random; } - /** - * Initialise the key generator for DSA 2. - * <p> - * Use this init method if you need to generate parameters for DSA 2 keys. - * </p> - * - * @param params DSA 2 key generation parameters. - */ + /// <summary>Initialise the generator for DSA 2</summary> + /// <remarks>You must use this Init method if you need to generate parameters for DSA 2 keys</remarks> + /// <param name="parameters">An instance of <c>DsaParameterGenerationParameters</c> used to configure this generator</param> public virtual void Init(DsaParameterGenerationParameters parameters) { // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1? @@ -84,35 +77,8 @@ namespace Org.BouncyCastle.Crypto.Generators throw new InvalidOperationException("Digest output size too small for value of N"); } -// /** -// * 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. - * <p> - * Note: can take a while...</p> - */ + /// <summary>Generates a set of <c>DsaParameters</c></summary> + /// <remarks>Can take a while...</remarks> public virtual DsaParameters GenerateParameters() { return use186_3 @@ -242,8 +208,7 @@ namespace Org.BouncyCastle.Crypto.Generators BigInteger U = new BigInteger(1, output).Mod(BigInteger.One.ShiftLeft(N - 1)); // 7. q = 2^(N–1) + U + 1 – ( U mod 2). - BigInteger q = BigInteger.One.ShiftLeft(N - 1).Add(U).Add(BigInteger.One).Subtract( - U.Mod(BigInteger.Two)); + BigInteger q = U.SetBit(0).SetBit(N - 1); // 8. Test whether or not q is prime as specified in Appendix C.3. // TODO Review C.3 for primality checking diff --git a/crypto/src/crypto/generators/ECKeyPairGenerator.cs b/crypto/src/crypto/generators/ECKeyPairGenerator.cs
index 6e777c74c..d4afff750 100644 --- a/crypto/src/crypto/generators/ECKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/ECKeyPairGenerator.cs
@@ -78,6 +78,7 @@ namespace Org.BouncyCastle.Crypto.Generators X9ECParameters ecps = FindECCurveByOid(oid); + this.publicKeyParamSet = oid; this.parameters = new ECDomainParameters( ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed()); } diff --git a/crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs b/crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs
index 5878da64b..013b81810 100644 --- a/crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs +++ b/crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs
@@ -3,71 +3,86 @@ using System; using Org.BouncyCastle.Asn1.CryptoPro; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; 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; + /** + * 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); + 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? - } + if (parameters.Strength != kgp.Parameters.P.BitLength - 1) + { + // TODO Should we complain? + } - this.param = kgp; - } - } + this.param = kgp; + } + } - public AsymmetricCipherKeyPair GenerateKeyPair() - { - SecureRandom random = param.Random; - Gost3410Parameters gost3410Params = param.Parameters; + 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 q = gost3410Params.Q, x; - BigInteger p = gost3410Params.P; - BigInteger a = gost3410Params.A; + int minWeight = 64; + for (;;) + { + x = new BigInteger(256, random); - // calculate the public key. - BigInteger y = a.ModPow(x, p); + if (x.SignValue < 1 || x.CompareTo(q) >= 0) + continue; - if (param.PublicKeyParamSet != null) - { - return new AsymmetricCipherKeyPair( - new Gost3410PublicKeyParameters(y, param.PublicKeyParamSet), - new Gost3410PrivateKeyParameters(x, param.PublicKeyParamSet)); - } + /* + * Require a minimum weight of the NAF representation, since low-weight primes may be + * weak against a version of the number-field-sieve for the discrete-logarithm-problem. + * + * See "The number field sieve for integers of low weight", Oliver Schirokauer. + */ + if (WNafUtilities.GetNafWeight(x) < minWeight) + continue; - return new AsymmetricCipherKeyPair( - new Gost3410PublicKeyParameters(y, gost3410Params), - new Gost3410PrivateKeyParameters(x, gost3410Params)); - } - } + break; + } + + 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/crypto/src/crypto/generators/RsaKeyPairGenerator.cs b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
index e870f1c08..2613b902b 100644 --- a/crypto/src/crypto/generators/RsaKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
@@ -4,6 +4,7 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Generators { @@ -11,117 +12,127 @@ namespace Org.BouncyCastle.Crypto.Generators * an RSA key pair generator. */ public class RsaKeyPairGenerator - : IAsymmetricCipherKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator { - private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001); - private const int DefaultTests = 12; + private static readonly int[] SPECIAL_E_VALUES = new int[]{ 3, 5, 17, 257, 65537 }; + private static readonly int SPECIAL_E_HIGHEST = SPECIAL_E_VALUES[SPECIAL_E_VALUES.Length - 1]; + private static readonly int SPECIAL_E_BITS = BigInteger.ValueOf(SPECIAL_E_HIGHEST).BitLength; - private RsaKeyGenerationParameters param; + protected static readonly BigInteger One = BigInteger.One; + protected static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001); + protected const int DefaultTests = 100; - public void Init( + protected RsaKeyGenerationParameters parameters; + + public virtual void Init( KeyGenerationParameters parameters) { if (parameters is RsaKeyGenerationParameters) { - this.param = (RsaKeyGenerationParameters)parameters; + this.parameters = (RsaKeyGenerationParameters)parameters; } else { - this.param = new RsaKeyGenerationParameters( + this.parameters = new RsaKeyGenerationParameters( DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests); } } - public AsymmetricCipherKeyPair GenerateKeyPair() + public virtual 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 qBitlength = strength >> 1; - int pBitlength = strength - qBitlength; - int mindiffbits = strength / 3; - int minWeight = strength >> 2; - - 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") - - p = ChooseRandomPrime(pBitlength, e); - - // - // Generate a modulus of the required length - // for (;;) { - q = ChooseRandomPrime(qBitlength, e); + // + // p and q values should have a length of half the strength in bits + // + int strength = parameters.Strength; + int pBitlength = (strength + 1) / 2; + int qBitlength = strength - pBitlength; + int mindiffbits = strength / 3; + int minWeight = strength >> 2; - // p and q should not be too close together (or equal!) - BigInteger diff = q.Subtract(p).Abs(); - if (diff.BitLength < mindiffbits) - continue; + BigInteger e = parameters.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") + + BigInteger p = ChooseRandomPrime(pBitlength, e); + BigInteger q, n; // - // calculate the modulus + // generate a modulus of the required length // - n = p.Multiply(q); - - if (n.BitLength != strength) + for (;;) { + q = ChooseRandomPrime(qBitlength, e); + + // p and q should not be too close together (or equal!) + BigInteger diff = q.Subtract(p).Abs(); + if (diff.BitLength < mindiffbits) + continue; + // - // if we get here our primes aren't big enough, make the largest - // of the two p and try again + // calculate the modulus // - p = p.Max(q); - continue; + n = p.Multiply(q); + + if (n.BitLength != strength) + { + // + // if we get here our primes aren't big enough, make the largest + // of the two p and try again + // + p = p.Max(q); + continue; + } + + /* + * Require a minimum weight of the NAF representation, since low-weight composites may + * be weak against a version of the number-field-sieve for factoring. + * + * See "The number field sieve for integers of low weight", Oliver Schirokauer. + */ + if (WNafUtilities.GetNafWeight(n) < minWeight) + { + p = ChooseRandomPrime(pBitlength, e); + continue; + } + + break; } - /* - * Require a minimum weight of the NAF representation, since low-weight composites may - * be weak against a version of the number-field-sieve for factoring. - * - * See "The number field sieve for integers of low weight", Oliver Schirokauer. - */ - if (WNafUtilities.GetNafWeight(n) < minWeight) + if (p.CompareTo(q) < 0) { - p = ChooseRandomPrime(pBitlength, e); - continue; + BigInteger tmp = p; + p = q; + q = tmp; } - break; - } - - 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); + BigInteger pSub1 = p.Subtract(One); + BigInteger qSub1 = q.Subtract(One); + //BigInteger phi = pSub1.Multiply(qSub1); + BigInteger gcd = pSub1.Gcd(qSub1); + BigInteger lcm = pSub1.Divide(gcd).Multiply(qSub1); - // - // calculate the private exponent - // - d = e.ModInverse(phi); + // + // calculate the private exponent + // + BigInteger d = e.ModInverse(lcm); - // - // calculate the CRT factors - // - BigInteger dP, dQ, qInv; + if (d.BitLength <= qBitlength) + continue; - dP = d.Remainder(pSub1); - dQ = d.Remainder(qSub1); - qInv = q.ModInverse(p); + // + // calculate the CRT factors + // + BigInteger dP = d.Remainder(pSub1); + BigInteger dQ = d.Remainder(qSub1); + BigInteger qInv = q.ModInverse(p); - return new AsymmetricCipherKeyPair( - new RsaKeyParameters(false, n, e), - new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv)); + return new AsymmetricCipherKeyPair( + new RsaKeyParameters(false, n, e), + new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv)); + } } /// <summary>Choose a random prime value for use with RSA</summary> @@ -130,17 +141,19 @@ namespace Org.BouncyCastle.Crypto.Generators /// <returns>a prime p, with (p-1) relatively prime to e</returns> protected virtual BigInteger ChooseRandomPrime(int bitlength, BigInteger e) { + bool eIsKnownOddPrime = (e.BitLength <= SPECIAL_E_BITS) && Arrays.Contains(SPECIAL_E_VALUES, e.IntValue); + for (;;) { - BigInteger p = new BigInteger(bitlength, 1, param.Random); + BigInteger p = new BigInteger(bitlength, 1, parameters.Random); - if (p.Mod(e).Equals(BigInteger.One)) + if (p.Mod(e).Equals(One)) continue; - if (!p.IsProbablePrime(param.Certainty)) + if (!p.IsProbablePrime(parameters.Certainty)) continue; - if (!e.Gcd(p.Subtract(BigInteger.One)).Equals(BigInteger.One)) + if (!eIsKnownOddPrime && !e.Gcd(p.Subtract(One)).Equals(One)) continue; return p; diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs
index eb340ddbc..f2c3990c6 100644 --- a/crypto/src/crypto/macs/GMac.cs +++ b/crypto/src/crypto/macs/GMac.cs
@@ -8,104 +8,105 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Macs { - /// <summary> - /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication - /// 800-38D. - /// </summary> - /// <remarks> - /// GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac - /// is processed as additional authenticated data with the underlying GCM block cipher). - /// </remarks> - public class GMac - : IMac - { - private readonly GcmBlockCipher cipher; - private readonly int macSizeBits; + /// <summary> + /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication + /// 800-38D. + /// </summary> + /// <remarks> + /// GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac + /// is processed as additional authenticated data with the underlying GCM block cipher). + /// </remarks> + public class GMac + : IMac + { + private readonly GcmBlockCipher cipher; + private readonly int macSizeBits; - /// <summary> - /// Creates a GMAC based on the operation of a block cipher in GCM mode. - /// </summary> - /// <remarks> - /// This will produce an authentication code the length of the block size of the cipher. - /// </remarks> - /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param> - public GMac(GcmBlockCipher cipher) - : this(cipher, 128) - { - } + /// <summary> + /// Creates a GMAC based on the operation of a block cipher in GCM mode. + /// </summary> + /// <remarks> + /// This will produce an authentication code the length of the block size of the cipher. + /// </remarks> + /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param> + public GMac(GcmBlockCipher cipher) + : this(cipher, 128) + { + } - /// <summary> - /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. - /// </summary> - /// <remarks> - /// This will produce an authentication code the length of the block size of the cipher. - /// </remarks> - /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param> - /// <param name="macSizeBits">the mac size to generate, in bits. Must be a multiple of 8, between 96 and 128 (inclusive).</param> - public GMac(GcmBlockCipher cipher, int macSizeBits) - { - this.cipher = cipher; - this.macSizeBits = macSizeBits; - } + /// <summary> + /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. + /// </summary> + /// <remarks> + /// This will produce an authentication code the length of the block size of the cipher. + /// </remarks> + /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param> + /// <param name="macSizeBits">the mac size to generate, in bits. Must be a multiple of 8, between 32 and 128 (inclusive). + /// Sizes less than 96 are not recommended, but are supported for specialized applications.</param> + public GMac(GcmBlockCipher cipher, int macSizeBits) + { + this.cipher = cipher; + this.macSizeBits = macSizeBits; + } - /// <summary> - /// Initialises the GMAC - requires a <see cref="Org.BouncyCastle.Crypto.Parameters.ParametersWithIV"/> - /// providing a <see cref="Org.BouncyCastle.Crypto.Parameters.KeyParameter"/> and a nonce. - /// </summary> - public void Init(ICipherParameters parameters) - { - if (parameters is ParametersWithIV) - { - ParametersWithIV param = (ParametersWithIV)parameters; + /// <summary> + /// Initialises the GMAC - requires a <see cref="Org.BouncyCastle.Crypto.Parameters.ParametersWithIV"/> + /// providing a <see cref="Org.BouncyCastle.Crypto.Parameters.KeyParameter"/> and a nonce. + /// </summary> + public void Init(ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)parameters; - byte[] iv = param.GetIV(); - KeyParameter keyParam = (KeyParameter)param.Parameters; + byte[] iv = param.GetIV(); + KeyParameter keyParam = (KeyParameter)param.Parameters; - // GCM is always operated in encrypt mode to calculate MAC - cipher.Init(true, new AeadParameters(keyParam, macSizeBits, iv)); - } - else - { - throw new ArgumentException("GMAC requires ParametersWithIV"); - } - } + // GCM is always operated in encrypt mode to calculate MAC + cipher.Init(true, new AeadParameters(keyParam, macSizeBits, iv)); + } + else + { + throw new ArgumentException("GMAC requires ParametersWithIV"); + } + } - public string AlgorithmName - { - get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; } - } + public string AlgorithmName + { + get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; } + } - public int GetMacSize() - { - return macSizeBits / 8; - } + public int GetMacSize() + { + return macSizeBits / 8; + } - public void Update(byte input) - { - cipher.ProcessAadByte(input); - } + public void Update(byte input) + { + cipher.ProcessAadByte(input); + } - public void BlockUpdate(byte[] input, int inOff, int len) - { - cipher.ProcessAadBytes(input, inOff, len); - } + public void BlockUpdate(byte[] input, int inOff, int len) + { + cipher.ProcessAadBytes(input, inOff, len); + } - public int DoFinal(byte[] output, int outOff) - { - try - { - return cipher.DoFinal(output, outOff); - } - catch (InvalidCipherTextException e) - { - // Impossible in encrypt mode - throw new InvalidOperationException(e.ToString()); - } - } + public int DoFinal(byte[] output, int outOff) + { + try + { + return cipher.DoFinal(output, outOff); + } + catch (InvalidCipherTextException e) + { + // Impossible in encrypt mode + throw new InvalidOperationException(e.ToString()); + } + } - public void Reset() - { - cipher.Reset(); - } - } + public void Reset() + { + cipher.Reset(); + } + } } diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs
index 2d453b6ad..1a951ca04 100644 --- a/crypto/src/crypto/macs/Poly1305.cs +++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -7,266 +7,285 @@ using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Macs { - /// <summary> - /// Poly1305 message authentication code, designed by D. J. Bernstein. - /// </summary> - /// <remarks> - /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key - /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106 - /// effective key bits) used in the authenticator. - /// - /// The polynomial calculation in this implementation is adapted from the public domain <a - /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation - /// by Andrew M (@floodyberry). - /// </remarks> - /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/> - public class Poly1305 - : IMac - { - private const int BLOCK_SIZE = 16; - - private readonly IBlockCipher cipher; - - private readonly byte[] singleByte = new byte[1]; - - // Initialised state - - /** Polynomial key */ - private uint r0, r1, r2, r3, r4; - - /** Precomputed 5 * r[1..4] */ - private uint s1, s2, s3, s4; - - /** Encrypted nonce */ - private uint k0, k1, k2, k3; - - // Accumulating state - - /** Current block of buffered input */ - private byte[] currentBlock = new byte[BLOCK_SIZE]; - - /** Current offset in input buffer */ - private int currentBlockOffset = 0; - - /** Polynomial accumulator */ - private uint h0, h1, h2, h3, h4; - - /** - * Constructs a Poly1305 MAC, using a 128 bit block cipher. - */ - public Poly1305(IBlockCipher cipher) - { - if (cipher.GetBlockSize() != BLOCK_SIZE) - { - throw new ArgumentException("Poly1305 requires a 128 bit block cipher."); - } - this.cipher = cipher; - } - - /// <summary> - /// Initialises the Poly1305 MAC. - /// </summary> - /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with - /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param> - public void Init(ICipherParameters parameters) - { - byte[] nonce; - byte[] key; - if ((parameters is ParametersWithIV) && ((ParametersWithIV)parameters).Parameters is KeyParameter) - { - nonce = ((ParametersWithIV)parameters).GetIV(); - key = ((KeyParameter)((ParametersWithIV)parameters).Parameters).GetKey(); - } - else - { - throw new ArgumentException("Poly1305 requires a key and and IV."); - } - - setKey(key, nonce); - Reset(); - } - - private void setKey(byte[] key, byte[] nonce) - { - if (nonce.Length != BLOCK_SIZE) - { - throw new ArgumentException("Poly1305 requires a 128 bit IV."); - } - Poly1305KeyGenerator.CheckKey(key); - - // Extract r portion of key - uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0); - uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4); - uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8); - uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12); - - r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; - r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; - r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; - r3 = t2 & 0x3f03fff; t3 >>= 8; - r4 = t3 & 0x00fffff; - - // Precompute multipliers - s1 = r1 * 5; - s2 = r2 * 5; - s3 = r3 * 5; - s4 = r4 * 5; - - // Compute encrypted nonce - byte[] cipherKey = new byte[BLOCK_SIZE]; - Array.Copy(key, 0, cipherKey, 0, cipherKey.Length); - - cipher.Init(true, new KeyParameter(cipherKey)); - cipher.ProcessBlock(nonce, 0, cipherKey, 0); - - k0 = Pack.LE_To_UInt32(cipherKey, 0); - k1 = Pack.LE_To_UInt32(cipherKey, 4); - k2 = Pack.LE_To_UInt32(cipherKey, 8); - k3 = Pack.LE_To_UInt32(cipherKey, 12); - } - - public string AlgorithmName - { - get { return "Poly1305-" + cipher.AlgorithmName; } - } - - public int GetMacSize() - { - return BLOCK_SIZE; - } - - public void Update(byte input) - { - singleByte[0] = input; - BlockUpdate(singleByte, 0, 1); - } - - public void BlockUpdate(byte[] input, int inOff, int len) - { - int copied = 0; - while (len > copied) - { - if (currentBlockOffset == BLOCK_SIZE) - { - processBlock(); - currentBlockOffset = 0; - } - - int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset); - Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy); - copied += toCopy; - currentBlockOffset += toCopy; - } - - } - - private void processBlock() - { - if (currentBlockOffset < BLOCK_SIZE) - { - currentBlock[currentBlockOffset] = 1; - for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++) - { - currentBlock[i] = 0; - } - } - - ulong t0 = Pack.LE_To_UInt32(currentBlock, 0); - ulong t1 = Pack.LE_To_UInt32(currentBlock, 4); - ulong t2 = Pack.LE_To_UInt32(currentBlock, 8); - ulong t3 = Pack.LE_To_UInt32(currentBlock, 12); - - h0 += (uint)(t0 & 0x3ffffffU); - h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff); - h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff); - h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff); - h4 += (uint)(t3 >> 8); - - if (currentBlockOffset == BLOCK_SIZE) - { - h4 += (1 << 24); - } - - ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1); - ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2); - ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3); - ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4); - ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0); - - ulong b; - h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26); - tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26); - tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26); - tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26); - tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26); - h0 += (uint)(b * 5); - } - - public int DoFinal(byte[] output, int outOff) - { - if (outOff + BLOCK_SIZE > output.Length) - { - throw new DataLengthException("Output buffer is too short."); - } - - if (currentBlockOffset > 0) - { - // Process padded block - processBlock(); - } - - ulong f0, f1, f2, f3; - - uint b = h0 >> 26; - h0 = h0 & 0x3ffffff; - h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; - h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; - h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; - h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; - h0 += b * 5; - - uint g0, g1, g2, g3, g4; - g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; - g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; - g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; - g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; - g4 = h4 + b - (1 << 26); - - b = (g4 >> 31) - 1; - uint nb = ~b; - h0 = (h0 & nb) | (g0 & b); - h1 = (h1 & nb) | (g1 & b); - h2 = (h2 & nb) | (g2 & b); - h3 = (h3 & nb) | (g3 & b); - h4 = (h4 & nb) | (g4 & b); - - f0 = ((h0 ) | (h1 << 26)) + (ulong)k0; - f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1; - f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2; - f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3; - - Pack.UInt32_To_LE((uint)f0, output, outOff); - f1 += (f0 >> 32); - Pack.UInt32_To_LE((uint)f1, output, outOff + 4); - f2 += (f1 >> 32); - Pack.UInt32_To_LE((uint)f2, output, outOff + 8); - f3 += (f2 >> 32); - Pack.UInt32_To_LE((uint)f3, output, outOff + 12); - - Reset(); - return BLOCK_SIZE; - } - - public void Reset() - { - currentBlockOffset = 0; - - h0 = h1 = h2 = h3 = h4 = 0; - } - - private static ulong mul32x32_64(uint i1, uint i2) - { - return ((ulong)i1) * i2; - } - } + /// <summary> + /// Poly1305 message authentication code, designed by D. J. Bernstein. + /// </summary> + /// <remarks> + /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key + /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106 + /// effective key bits) used in the authenticator. + /// + /// The polynomial calculation in this implementation is adapted from the public domain <a + /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation + /// by Andrew M (@floodyberry). + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/> + public class Poly1305 + : IMac + { + private const int BLOCK_SIZE = 16; + + private readonly IBlockCipher cipher; + + private readonly byte[] singleByte = new byte[1]; + + // Initialised state + + /** Polynomial key */ + private uint r0, r1, r2, r3, r4; + + /** Precomputed 5 * r[1..4] */ + private uint s1, s2, s3, s4; + + /** Encrypted nonce */ + private uint k0, k1, k2, k3; + + // Accumulating state + + /** Current block of buffered input */ + private byte[] currentBlock = new byte[BLOCK_SIZE]; + + /** Current offset in input buffer */ + private int currentBlockOffset = 0; + + /** Polynomial accumulator */ + private uint h0, h1, h2, h3, h4; + + /** + * Constructs a Poly1305 MAC, where the key passed to init() will be used directly. + */ + public Poly1305() + { + this.cipher = null; + } + + /** + * Constructs a Poly1305 MAC, using a 128 bit block cipher. + */ + public Poly1305(IBlockCipher cipher) + { + if (cipher.GetBlockSize() != BLOCK_SIZE) + { + throw new ArgumentException("Poly1305 requires a 128 bit block cipher."); + } + this.cipher = cipher; + } + + /// <summary> + /// Initialises the Poly1305 MAC. + /// </summary> + /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with + /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param> + public void Init(ICipherParameters parameters) + { + byte[] nonce = null; + + if (cipher != null) + { + if (!(parameters is ParametersWithIV)) + throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters"); + + ParametersWithIV ivParams = (ParametersWithIV)parameters; + nonce = ivParams.GetIV(); + parameters = ivParams.Parameters; + } + + if (!(parameters is KeyParameter)) + throw new ArgumentException("Poly1305 requires a key."); + + KeyParameter keyParams = (KeyParameter)parameters; + + SetKey(keyParams.GetKey(), nonce); + + Reset(); + } + + private void SetKey(byte[] key, byte[] nonce) + { + if (cipher != null && (nonce == null || nonce.Length != BLOCK_SIZE)) + throw new ArgumentException("Poly1305 requires a 128 bit IV."); + + Poly1305KeyGenerator.CheckKey(key); + + // Extract r portion of key + uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0); + uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4); + uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8); + uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12); + + r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; + r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; + r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; + r3 = t2 & 0x3f03fff; t3 >>= 8; + r4 = t3 & 0x00fffff; + + // Precompute multipliers + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + byte[] kBytes; + if (cipher == null) + { + kBytes = key; + } + else + { + // Compute encrypted nonce + kBytes = new byte[BLOCK_SIZE]; + cipher.Init(true, new KeyParameter(key, 0, BLOCK_SIZE)); + cipher.ProcessBlock(nonce, 0, kBytes, 0); + } + + k0 = Pack.LE_To_UInt32(kBytes, 0); + k1 = Pack.LE_To_UInt32(kBytes, 4); + k2 = Pack.LE_To_UInt32(kBytes, 8); + k3 = Pack.LE_To_UInt32(kBytes, 12); + } + + public string AlgorithmName + { + get { return cipher == null ? "Poly1305" : "Poly1305-" + cipher.AlgorithmName; } + } + + public int GetMacSize() + { + return BLOCK_SIZE; + } + + public void Update(byte input) + { + singleByte[0] = input; + BlockUpdate(singleByte, 0, 1); + } + + public void BlockUpdate(byte[] input, int inOff, int len) + { + int copied = 0; + while (len > copied) + { + if (currentBlockOffset == BLOCK_SIZE) + { + processBlock(); + currentBlockOffset = 0; + } + + int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset); + Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy); + copied += toCopy; + currentBlockOffset += toCopy; + } + + } + + private void processBlock() + { + if (currentBlockOffset < BLOCK_SIZE) + { + currentBlock[currentBlockOffset] = 1; + for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++) + { + currentBlock[i] = 0; + } + } + + ulong t0 = Pack.LE_To_UInt32(currentBlock, 0); + ulong t1 = Pack.LE_To_UInt32(currentBlock, 4); + ulong t2 = Pack.LE_To_UInt32(currentBlock, 8); + ulong t3 = Pack.LE_To_UInt32(currentBlock, 12); + + h0 += (uint)(t0 & 0x3ffffffU); + h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff); + h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff); + h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff); + h4 += (uint)(t3 >> 8); + + if (currentBlockOffset == BLOCK_SIZE) + { + h4 += (1 << 24); + } + + ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1); + ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2); + ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3); + ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4); + ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0); + + ulong b; + h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26); + tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26); + tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26); + tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26); + tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26); + h0 += (uint)(b * 5); + } + + public int DoFinal(byte[] output, int outOff) + { + if (outOff + BLOCK_SIZE > output.Length) + { + throw new DataLengthException("Output buffer is too short."); + } + + if (currentBlockOffset > 0) + { + // Process padded block + processBlock(); + } + + ulong f0, f1, f2, f3; + + uint b = h0 >> 26; + h0 = h0 & 0x3ffffff; + h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += b * 5; + + uint g0, g1, g2, g3, g4; + g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + b - (1 << 26); + + b = (g4 >> 31) - 1; + uint nb = ~b; + h0 = (h0 & nb) | (g0 & b); + h1 = (h1 & nb) | (g1 & b); + h2 = (h2 & nb) | (g2 & b); + h3 = (h3 & nb) | (g3 & b); + h4 = (h4 & nb) | (g4 & b); + + f0 = ((h0 ) | (h1 << 26)) + (ulong)k0; + f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1; + f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2; + f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3; + + Pack.UInt32_To_LE((uint)f0, output, outOff); + f1 += (f0 >> 32); + Pack.UInt32_To_LE((uint)f1, output, outOff + 4); + f2 += (f1 >> 32); + Pack.UInt32_To_LE((uint)f2, output, outOff + 8); + f3 += (f2 >> 32); + Pack.UInt32_To_LE((uint)f3, output, outOff + 12); + + Reset(); + return BLOCK_SIZE; + } + + public void Reset() + { + currentBlockOffset = 0; + + h0 = h1 = h2 = h3 = h4 = 0; + } + + private static ulong mul32x32_64(uint i1, uint i2) + { + return ((ulong)i1) * i2; + } + } } diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index 653d75cb9..19e273d7c 100644 --- a/crypto/src/crypto/modes/CcmBlockCipher.cs +++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -61,6 +61,7 @@ namespace Org.BouncyCastle.Crypto.Modes { this.forEncryption = forEncryption; + ICipherParameters cipherParameters; if (parameters is AeadParameters) { AeadParameters param = (AeadParameters) parameters; @@ -68,7 +69,7 @@ namespace Org.BouncyCastle.Crypto.Modes nonce = param.GetNonce(); initialAssociatedText = param.GetAssociatedText(); macSize = param.MacSize / 8; - keyParam = param.Key; + cipherParameters = param.Key; } else if (parameters is ParametersWithIV) { @@ -77,17 +78,25 @@ namespace Org.BouncyCastle.Crypto.Modes nonce = param.GetIV(); initialAssociatedText = null; macSize = macBlock.Length / 2; - keyParam = param.Parameters; + cipherParameters = param.Parameters; } else { throw new ArgumentException("invalid parameters passed to CCM"); } + // NOTE: Very basic support for key re-use, but no performance gain from it + if (cipherParameters != null) + { + keyParam = cipherParameters; + } + if (nonce == null || nonce.Length < 7 || nonce.Length > 13) { throw new ArgumentException("nonce must have length from 7 to 13 octets"); } + + Reset(); } public virtual string AlgorithmName @@ -128,6 +137,8 @@ namespace Org.BouncyCastle.Crypto.Modes byte[] outBytes, int outOff) { + Check.DataLength(inBytes, inOff, inLen, "Input buffer too short"); + data.Write(inBytes, inOff, inLen); return 0; @@ -137,13 +148,11 @@ namespace Org.BouncyCastle.Crypto.Modes byte[] outBytes, int outOff) { - byte[] enc = ProcessPacket(data.GetBuffer(), 0, (int)data.Position); - - Array.Copy(enc, 0, outBytes, outOff, enc.Length); + int len = ProcessPacket(data.GetBuffer(), 0, (int)data.Position, outBytes, outOff); Reset(); - return enc.Length; + return len; } public virtual void Reset() @@ -161,11 +170,7 @@ namespace Org.BouncyCastle.Crypto.Modes */ public virtual byte[] GetMac() { - byte[] mac = new byte[macSize]; - - Array.Copy(macBlock, 0, mac, 0, mac.Length); - - return mac; + return Arrays.CopyOfRange(macBlock, 0, macSize); } public virtual int GetUpdateOutputSize( @@ -174,7 +179,7 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } - public int GetOutputSize( + public virtual int GetOutputSize( int len) { int totalData = (int)data.Length + len; @@ -187,10 +192,51 @@ namespace Org.BouncyCastle.Crypto.Modes return totalData < macSize ? 0 : totalData - macSize; } - public byte[] ProcessPacket( - byte[] input, - int inOff, - int inLen) + /** + * Process a packet of data for either CCM decryption or encryption. + * + * @param in data for processing. + * @param inOff offset at which data starts in the input array. + * @param inLen length of the data in the input array. + * @return a byte array containing the processed input.. + * @throws IllegalStateException if the cipher is not appropriately set up. + * @throws InvalidCipherTextException if the input data is truncated or the mac check fails. + */ + public virtual byte[] ProcessPacket(byte[] input, int inOff, int inLen) + { + byte[] output; + + if (forEncryption) + { + output = new byte[inLen + macSize]; + } + else + { + if (inLen < macSize) + throw new InvalidCipherTextException("data too short"); + + output = new byte[inLen - macSize]; + } + + ProcessPacket(input, inOff, inLen, output, 0); + + return output; + } + + /** + * Process a packet of data for either CCM decryption or encryption. + * + * @param in data for processing. + * @param inOff offset at which data starts in the input array. + * @param inLen length of the data in the input array. + * @param output output array. + * @param outOff offset into output array to start putting processed bytes. + * @return the number of bytes added to output. + * @throws IllegalStateException if the cipher is not appropriately set up. + * @throws InvalidCipherTextException if the input data is truncated or the mac check fails. + * @throws DataLengthException if output buffer too short. + */ + public virtual int ProcessPacket(byte[] input, int inOff, int inLen, byte[] output, int outOff) { // TODO: handle null keyParam (e.g. via RepeatedKeySpec) // Need to keep the CTR and CBC Mac parts around and reset @@ -213,42 +259,46 @@ namespace Org.BouncyCastle.Crypto.Modes IBlockCipher ctrCipher = new SicBlockCipher(cipher); ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv)); - int index = inOff; - int outOff = 0; - byte[] output; + int outputLen; + int inIndex = inOff; + int outIndex = outOff; if (forEncryption) { - output = new byte[inLen + macSize]; + outputLen = inLen + macSize; + Check.OutputLength(output, outOff, outputLen, "Output buffer too short."); - calculateMac(input, inOff, inLen, macBlock); + CalculateMac(input, inOff, inLen, macBlock); - ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); // S0 + byte[] encMac = new byte[BlockSize]; + ctrCipher.ProcessBlock(macBlock, 0, encMac, 0); // S0 - while (index < inLen - BlockSize) // S1... + while (inIndex < (inOff + inLen - BlockSize)) // S1... { - ctrCipher.ProcessBlock(input, index, output, outOff); - outOff += BlockSize; - index += BlockSize; + ctrCipher.ProcessBlock(input, inIndex, output, outIndex); + outIndex += BlockSize; + inIndex += BlockSize; } byte[] block = new byte[BlockSize]; - Array.Copy(input, index, block, 0, inLen - index); + Array.Copy(input, inIndex, block, 0, inLen + inOff - inIndex); ctrCipher.ProcessBlock(block, 0, block, 0); - Array.Copy(block, 0, output, outOff, inLen - index); + Array.Copy(block, 0, output, outIndex, inLen + inOff - inIndex); - outOff += inLen - index; - - Array.Copy(macBlock, 0, output, outOff, output.Length - outOff); + Array.Copy(encMac, 0, output, outOff + inLen, macSize); } else { - output = new byte[inLen - macSize]; + if (inLen < macSize) + throw new InvalidCipherTextException("data too short"); - Array.Copy(input, inOff + inLen - macSize, macBlock, 0, macSize); + outputLen = inLen - macSize; + Check.OutputLength(output, outOff, outputLen, "Output buffer too short."); + + Array.Copy(input, inOff + outputLen, macBlock, 0, macSize); ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); @@ -257,33 +307,33 @@ namespace Org.BouncyCastle.Crypto.Modes macBlock[i] = 0; } - while (outOff < output.Length - BlockSize) + while (inIndex < (inOff + outputLen - BlockSize)) { - ctrCipher.ProcessBlock(input, index, output, outOff); - outOff += BlockSize; - index += BlockSize; + ctrCipher.ProcessBlock(input, inIndex, output, outIndex); + outIndex += BlockSize; + inIndex += BlockSize; } byte[] block = new byte[BlockSize]; - Array.Copy(input, index, block, 0, output.Length - outOff); + Array.Copy(input, inIndex, block, 0, outputLen - (inIndex - inOff)); ctrCipher.ProcessBlock(block, 0, block, 0); - Array.Copy(block, 0, output, outOff, output.Length - outOff); + Array.Copy(block, 0, output, outIndex, outputLen - (inIndex - inOff)); byte[] calculatedMacBlock = new byte[BlockSize]; - calculateMac(output, 0, output.Length, calculatedMacBlock); + CalculateMac(output, outOff, outputLen, calculatedMacBlock); if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock)) throw new InvalidCipherTextException("mac check in CCM failed"); } - return output; + return outputLen; } - private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) + private int CalculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) { IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8); diff --git a/crypto/src/crypto/modes/CtsBlockCipher.cs b/crypto/src/crypto/modes/CtsBlockCipher.cs
index a32b49675..ff37844ab 100644 --- a/crypto/src/crypto/modes/CtsBlockCipher.cs +++ b/crypto/src/crypto/modes/CtsBlockCipher.cs
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Modes } /** - * process a single byte, producing an output block if neccessary. + * process a single byte, producing an output block if necessary. * * @param in the input byte. * @param out the space for any output that might be produced. diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index 5ccc69b66..624f385b5 100644 --- a/crypto/src/crypto/modes/EAXBlockCipher.cs +++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -54,7 +54,6 @@ namespace Org.BouncyCastle.Crypto.Modes 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); @@ -65,7 +64,7 @@ namespace Org.BouncyCastle.Crypto.Modes get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; } } - public IBlockCipher GetUnderlyingCipher() + public virtual IBlockCipher GetUnderlyingCipher() { return cipher; } @@ -107,6 +106,8 @@ namespace Org.BouncyCastle.Crypto.Modes throw new ArgumentException("invalid parameters passed to EAX"); } + bufBlock = new byte[forEncryption ? blockSize : (blockSize + macSize)]; + byte[] tag = new byte[blockSize]; // Key reuse implemented in CBC mode of underlying CMac @@ -117,16 +118,10 @@ namespace Org.BouncyCastle.Crypto.Modes mac.BlockUpdate(nonce, 0, nonce.Length); mac.DoFinal(nonceMac, 0); - tag[blockSize - 1] = (byte)Tag.H; - mac.BlockUpdate(tag, 0, blockSize); - - if (initialAssociatedText != null) - { - ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); - } - - // Same BlockCipher underlies this and the mac, so reuse last key on cipher + // Same BlockCipher underlies this and the mac, so reuse last key on cipher cipher.Init(true, new ParametersWithIV(null, nonceMac)); + + Reset(); } private void InitCipher() @@ -191,16 +186,16 @@ namespace Org.BouncyCastle.Crypto.Modes { if (cipherInitialized) { - throw new InvalidOperationException("AAD data cannot be added after encryption/decription processing has begun."); + throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); } mac.Update(input); } - public void ProcessAadBytes(byte[] inBytes, int inOff, int len) + public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) { if (cipherInitialized) { - throw new InvalidOperationException("AAD data cannot be added after encryption/decription processing has begun."); + throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); } mac.BlockUpdate(inBytes, inOff, len); } @@ -247,10 +242,11 @@ namespace Org.BouncyCastle.Crypto.Modes if (forEncryption) { - cipher.ProcessBlock(bufBlock, 0, tmp, 0); - cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize); + Check.OutputLength(outBytes, outOff, extra + macSize, "Output buffer too short"); + + cipher.ProcessBlock(bufBlock, 0, tmp, 0); - Array.Copy(tmp, 0, outBytes, outOff, extra); + Array.Copy(tmp, 0, outBytes, outOff, extra); mac.BlockUpdate(tmp, 0, extra); @@ -264,14 +260,18 @@ namespace Org.BouncyCastle.Crypto.Modes } else { - if (extra > macSize) + if (extra < macSize) + throw new InvalidCipherTextException("data too short"); + + Check.OutputLength(outBytes, outOff, extra - macSize, "Output buffer too short"); + + 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); + Array.Copy(tmp, 0, outBytes, outOff, extra - macSize); } CalculateMac(); @@ -331,6 +331,11 @@ namespace Org.BouncyCastle.Crypto.Modes if (bufOff == bufBlock.Length) { + Check.OutputLength(outBytes, outOff, blockSize, "Output buffer is too short"); + + // TODO Could move the ProcessByte(s) calls to here +// InitCipher(); + int size; if (forEncryption) @@ -346,10 +351,14 @@ namespace Org.BouncyCastle.Crypto.Modes size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff); } - bufOff = blockSize; - Array.Copy(bufBlock, blockSize, bufBlock, 0, blockSize); + bufOff = 0; + if (!forEncryption) + { + Array.Copy(bufBlock, blockSize, bufBlock, 0, macSize); + bufOff = macSize; + } - return size; + return size; } return 0; diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index 74b895e7b..8e6120eef 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -8,127 +8,129 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes { - /// <summary> - /// Implements the Galois/Counter mode (GCM) detailed in - /// NIST Special Publication 800-38D. - /// </summary> - public class GcmBlockCipher - : IAeadBlockCipher - { - private const int BlockSize = 16; + /// <summary> + /// Implements the Galois/Counter mode (GCM) detailed in + /// NIST Special Publication 800-38D. + /// </summary> + public class GcmBlockCipher + : IAeadBlockCipher + { + private const int BlockSize = 16; private readonly IBlockCipher cipher; - private readonly IGcmMultiplier multiplier; + private readonly IGcmMultiplier multiplier; private IGcmExponentiator exp; // These fields are set by Init and not modified by processing - private bool forEncryption; - private int macSize; - private byte[] nonce; - private byte[] initialAssociatedText; + private bool forEncryption; + private int macSize; + private byte[] nonce; + private byte[] initialAssociatedText; private byte[] H; - private byte[] J0; + private byte[] J0; // These fields are modified during processing - private byte[] bufBlock; - private byte[] macBlock; + private byte[] bufBlock; + private byte[] macBlock; private byte[] S, S_at, S_atPre; - private byte[] counter; - private int bufOff; - private ulong totalLength; + private byte[] counter; + private int bufOff; + private ulong totalLength; private byte[] atBlock; private int atBlockPos; private ulong atLength; private ulong atLengthPre; public GcmBlockCipher( - IBlockCipher c) - : this(c, null) - { - } - - public GcmBlockCipher( - IBlockCipher c, - IGcmMultiplier m) - { - if (c.GetBlockSize() != BlockSize) - throw new ArgumentException("cipher required with a block size of " + BlockSize + "."); - - if (m == null) - { - // TODO Consider a static property specifying default multiplier - m = new Tables8kGcmMultiplier(); - } - - this.cipher = c; - this.multiplier = m; - } - - public virtual string AlgorithmName - { - get { return cipher.AlgorithmName + "/GCM"; } - } - - public IBlockCipher GetUnderlyingCipher() - { - return cipher; - } - - public virtual int GetBlockSize() - { - return BlockSize; - } - - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) - { - this.forEncryption = forEncryption; - this.macBlock = null; + IBlockCipher c) + : this(c, null) + { + } + + public GcmBlockCipher( + IBlockCipher c, + IGcmMultiplier m) + { + if (c.GetBlockSize() != BlockSize) + throw new ArgumentException("cipher required with a block size of " + BlockSize + "."); + + if (m == null) + { + // TODO Consider a static property specifying default multiplier + m = new Tables8kGcmMultiplier(); + } + + this.cipher = c; + this.multiplier = m; + } + + public virtual string AlgorithmName + { + get { return cipher.AlgorithmName + "/GCM"; } + } + + public IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + public virtual int GetBlockSize() + { + return BlockSize; + } + + /// <remarks> + /// MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits. + /// Sizes less than 96 are not recommended, but are supported for specialized applications. + /// </remarks> + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + this.macBlock = null; KeyParameter keyParam; if (parameters is AeadParameters) - { - AeadParameters param = (AeadParameters)parameters; - - nonce = param.GetNonce(); - initialAssociatedText = param.GetAssociatedText(); - - int macSizeBits = param.MacSize; - if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0) - { - throw new ArgumentException("Invalid value for MAC size: " + macSizeBits); - } - - macSize = macSizeBits / 8; - keyParam = param.Key; - } - else if (parameters is ParametersWithIV) - { - ParametersWithIV param = (ParametersWithIV)parameters; - - nonce = param.GetIV(); + { + AeadParameters param = (AeadParameters)parameters; + + nonce = param.GetNonce(); + initialAssociatedText = param.GetAssociatedText(); + + int macSizeBits = param.MacSize; + if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0) + { + throw new ArgumentException("Invalid value for MAC size: " + macSizeBits); + } + + macSize = macSizeBits / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)parameters; + + nonce = param.GetIV(); initialAssociatedText = null; - macSize = 16; - keyParam = (KeyParameter)param.Parameters; - } - else - { - throw new ArgumentException("invalid parameters passed to GCM"); - } - - int bufLength = forEncryption ? BlockSize : (BlockSize + macSize); - this.bufBlock = new byte[bufLength]; - - if (nonce == null || nonce.Length < 1) - { - throw new ArgumentException("IV must be at least 1 byte"); - } - - // TODO This should be configurable by Init parameters - // (but must be 16 if nonce length not 12) (BlockSize?) -// this.tagLength = 16; + macSize = 16; + keyParam = (KeyParameter)param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to GCM"); + } + + int bufLength = forEncryption ? BlockSize : (BlockSize + macSize); + this.bufBlock = new byte[bufLength]; + + if (nonce == null || nonce.Length < 1) + { + throw new ArgumentException("IV must be at least 1 byte"); + } + + // TODO Restrict macSize to 16 if nonce length not 12? // Cipher always used in forward mode // if keyParam is null we're reusing the last key. @@ -136,28 +138,32 @@ namespace Org.BouncyCastle.Crypto.Modes { cipher.Init(true, keyParam); - this.H = new byte[BlockSize]; - cipher.ProcessBlock(H, 0, H, 0); + this.H = new byte[BlockSize]; + cipher.ProcessBlock(H, 0, H, 0); // if keyParam is null we're reusing the last key and the multiplier doesn't need re-init multiplier.Init(H); exp = null; } + else if (this.H == null) + { + throw new ArgumentException("Key must be specified in initial init"); + } this.J0 = new byte[BlockSize]; if (nonce.Length == 12) - { - Array.Copy(nonce, 0, J0, 0, nonce.Length); - this.J0[BlockSize - 1] = 0x01; - } - else - { + { + Array.Copy(nonce, 0, J0, 0, nonce.Length); + this.J0[BlockSize - 1] = 0x01; + } + else + { gHASH(J0, nonce, nonce.Length); - byte[] X = new byte[BlockSize]; + byte[] X = new byte[BlockSize]; Pack.UInt64_To_BE((ulong)nonce.Length * 8UL, X, 8); gHASHBlock(J0, X); - } + } this.S = new byte[BlockSize]; this.S_at = new byte[BlockSize]; @@ -174,16 +180,16 @@ namespace Org.BouncyCastle.Crypto.Modes { ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); } - } + } - public virtual byte[] GetMac() - { - return Arrays.Clone(macBlock); - } + public virtual byte[] GetMac() + { + return Arrays.Clone(macBlock); + } - public virtual int GetOutputSize( - int len) - { + public virtual int GetOutputSize( + int len) + { int totalData = len + bufOff; if (forEncryption) @@ -192,11 +198,11 @@ namespace Org.BouncyCastle.Crypto.Modes } return totalData < macSize ? 0 : totalData - macSize; - } + } public virtual int GetUpdateOutputSize( - int len) - { + int len) + { int totalData = len + bufOff; if (!forEncryption) { @@ -207,7 +213,7 @@ namespace Org.BouncyCastle.Crypto.Modes totalData -= macSize; } return totalData - totalData % BlockSize; - } + } public virtual void ProcessAadByte(byte input) { @@ -258,10 +264,10 @@ namespace Org.BouncyCastle.Crypto.Modes } public virtual int ProcessByte( - byte input, - byte[] output, - int outOff) - { + byte input, + byte[] output, + int outOff) + { bufBlock[bufOff] = input; if (++bufOff == bufBlock.Length) { @@ -269,15 +275,18 @@ namespace Org.BouncyCastle.Crypto.Modes return BlockSize; } return 0; - } + } public virtual int ProcessBytes( - byte[] input, - int inOff, - int len, - byte[] output, - int outOff) - { + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (input.Length < (inOff + len)) + throw new DataLengthException("Input buffer too short"); + int resultLen = 0; for (int i = 0; i < len; ++i) @@ -291,10 +300,11 @@ namespace Org.BouncyCastle.Crypto.Modes } return resultLen; - } + } private void OutputBlock(byte[] output, int offset) { + Check.OutputLength(output, offset, BlockSize, "Output buffer too short"); if (totalLength == 0) { InitCipher(); @@ -311,20 +321,27 @@ namespace Org.BouncyCastle.Crypto.Modes } } - public int DoFinal(byte[] output, int outOff) - { + public int DoFinal(byte[] output, int outOff) + { if (totalLength == 0) { InitCipher(); } int extra = bufOff; - if (!forEncryption) + + if (forEncryption) + { + Check.OutputLength(output, outOff, extra + macSize, "Output buffer too short"); + } + else { if (extra < macSize) throw new InvalidCipherTextException("data too short"); extra -= macSize; + + Check.OutputLength(output, outOff, extra, "Output buffer too short"); } if (extra > 0) @@ -375,52 +392,51 @@ namespace Org.BouncyCastle.Crypto.Modes } // Final gHASH - byte[] X = new byte[BlockSize]; + byte[] X = new byte[BlockSize]; Pack.UInt64_To_BE(atLength * 8UL, X, 0); Pack.UInt64_To_BE(totalLength * 8UL, X, 8); gHASHBlock(S, X); - // TODO Fix this if tagLength becomes configurable - // T = MSBt(GCTRk(J0,S)) - byte[] tag = new byte[BlockSize]; - cipher.ProcessBlock(J0, 0, tag, 0); - GcmUtilities.Xor(tag, S); - - int resultLen = extra; - - // We place into macBlock our calculated value for T - this.macBlock = new byte[macSize]; - Array.Copy(tag, 0, macBlock, 0, macSize); - - if (forEncryption) - { - // Append T to the message - Array.Copy(macBlock, 0, output, outOff + bufOff, macSize); - resultLen += macSize; - } - else - { - // Retrieve the T value from the message and compare to calculated one - byte[] msgMac = new byte[macSize]; - Array.Copy(bufBlock, extra, msgMac, 0, macSize); - if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac)) - throw new InvalidCipherTextException("mac check in GCM failed"); - } - - Reset(false); - - return resultLen; - } - - public virtual void Reset() - { - Reset(true); - } - - private void Reset( - bool clearMac) - { + // T = MSBt(GCTRk(J0,S)) + byte[] tag = new byte[BlockSize]; + cipher.ProcessBlock(J0, 0, tag, 0); + GcmUtilities.Xor(tag, S); + + int resultLen = extra; + + // We place into macBlock our calculated value for T + this.macBlock = new byte[macSize]; + Array.Copy(tag, 0, macBlock, 0, macSize); + + if (forEncryption) + { + // Append T to the message + Array.Copy(macBlock, 0, output, outOff + bufOff, macSize); + resultLen += macSize; + } + else + { + // Retrieve the T value from the message and compare to calculated one + byte[] msgMac = new byte[macSize]; + Array.Copy(bufBlock, extra, msgMac, 0, macSize); + if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac)) + throw new InvalidCipherTextException("mac check in GCM failed"); + } + + Reset(false); + + return resultLen; + } + + public virtual void Reset() + { + Reset(true); + } + + private void Reset( + bool clearMac) + { cipher.Reset(); S = new byte[BlockSize]; @@ -448,7 +464,7 @@ namespace Org.BouncyCastle.Crypto.Modes { ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); } - } + } private void gCTRBlock(byte[] block, byte[] output, int outOff) { @@ -507,5 +523,5 @@ namespace Org.BouncyCastle.Crypto.Modes cipher.ProcessBlock(counter, 0, tmp, 0); return tmp; } - } + } } diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs
index 8fb6f213f..e7dc466e6 100644 --- a/crypto/src/crypto/modes/OCBBlockCipher.cs +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -7,9 +7,8 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes { /** - * An implementation of the "work in progress" Internet-Draft <a - * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-07">The OCB Authenticated-Encryption - * Algorithm</a>, licensed per: + * An implementation of <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB + * Authenticated-Encryption Algorithm</a>, licensed per: * * <blockquote><p><a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for * Open-Source Software Implementations of OCB</a> (Jan 9, 2013) - 'License 1'<br/> @@ -71,9 +70,8 @@ namespace Org.BouncyCastle.Crypto.Modes throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "hashCipher"); if (mainCipher == null) throw new ArgumentNullException("mainCipher"); - if (mainCipher.GetBlockSize() != BLOCK_SIZE) { + if (mainCipher.GetBlockSize() != BLOCK_SIZE) throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "mainCipher"); - } if (!hashCipher.AlgorithmName.Equals(mainCipher.AlgorithmName)) throw new ArgumentException("'hashCipher' and 'mainCipher' must be the same algorithm"); @@ -94,6 +92,7 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void Init(bool forEncryption, ICipherParameters parameters) { + bool oldForEncryption = this.forEncryption; this.forEncryption = forEncryption; this.macBlock = null; @@ -145,20 +144,18 @@ namespace Org.BouncyCastle.Crypto.Modes * KEY-DEPENDENT INITIALISATION */ - // if keyParam is null we're reusing the last key. if (keyParameter != null) { - // TODO + // hashCipher always used in forward mode + hashCipher.Init(true, keyParameter); + mainCipher.Init(forEncryption, keyParameter); + KtopInput = null; } - else + else if (oldForEncryption != forEncryption) { - KtopInput = null; + throw new ArgumentException("cannot change encrypting state without providing key."); } - // hashCipher always used in forward mode - hashCipher.Init(true, keyParameter); - mainCipher.Init(forEncryption, keyParameter); - this.L_Asterisk = new byte[16]; hashCipher.ProcessBlock(L_Asterisk, 0, L_Asterisk, 0); @@ -358,6 +355,7 @@ namespace Org.BouncyCastle.Crypto.Modes Xor(mainBlock, Pad); + Check.OutputLength(output, outOff, mainBlockPos, "Output buffer too short"); Array.Copy(mainBlock, 0, output, outOff, mainBlockPos); if (!forEncryption) @@ -385,6 +383,8 @@ namespace Org.BouncyCastle.Crypto.Modes if (forEncryption) { + Check.OutputLength(output, outOff, resultLen + macSize, "Output buffer too short"); + // Append tag to the message Array.Copy(macBlock, 0, output, outOff + resultLen, macSize); resultLen += macSize; @@ -434,6 +434,8 @@ namespace Org.BouncyCastle.Crypto.Modes protected virtual void ProcessMainBlock(byte[] output, int outOff) { + Check.DataLength(output, outOff, BLOCK_SIZE, "Output buffer too short"); + /* * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks */ @@ -528,10 +530,11 @@ namespace Org.BouncyCastle.Crypto.Modes } int n = 0; - while ((x & 1L) == 0L) + ulong ux = (ulong)x; + while ((ux & 1UL) == 0UL) { ++n; - x >>= 1; + ux >>= 1; } return n; } diff --git a/crypto/src/crypto/modes/SicBlockCipher.cs b/crypto/src/crypto/modes/SicBlockCipher.cs
index 07c5d1978..da7ed7859 100644 --- a/crypto/src/crypto/modes/SicBlockCipher.cs +++ b/crypto/src/crypto/modes/SicBlockCipher.cs
@@ -5,52 +5,52 @@ 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; + /** + * 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]; - } + /** + * 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; - } + /** + * 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); + 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(); @@ -59,57 +59,57 @@ namespace Org.BouncyCastle.Crypto.Modes { cipher.Init(true, ivParam.Parameters); } - } - else - { - throw new ArgumentException("SIC mode requires ParametersWithIV", "parameters"); - } - } + } + else + { + throw new ArgumentException("SIC mode requires ParametersWithIV", "parameters"); + } + } - public string AlgorithmName - { - get { return cipher.AlgorithmName + "/SIC"; } - } + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/SIC"; } + } - public bool IsPartialBlockOkay - { - get { return true; } - } + public bool IsPartialBlockOkay + { + get { return true; } + } - public int GetBlockSize() - { - return cipher.GetBlockSize(); - } + public int GetBlockSize() + { + return cipher.GetBlockSize(); + } - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) - { - cipher.ProcessBlock(counter, 0, counterOut, 0); + 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]); - } + // + // 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) - { - } + // Increment the counter + int j = counter.Length; + while (--j >= 0 && ++counter[j] == 0) + { + } - return counter.Length; - } + return counter.Length; + } - public void Reset() - { - Array.Copy(IV, 0, counter, 0, counter.Length); - cipher.Reset(); - } - } + public void Reset() + { + Array.Copy(IV, 0, counter, 0, counter.Length); + cipher.Reset(); + } + } } diff --git a/crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs b/crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
index 98049e1db..5660a1f84 100644 --- a/crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs +++ b/crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
@@ -4,37 +4,37 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes.Gcm { - public class BasicGcmExponentiator - : IGcmExponentiator - { - private byte[] x; + public class BasicGcmExponentiator + : IGcmExponentiator + { + private uint[] x; - public void Init(byte[] x) - { - this.x = Arrays.Clone(x); - } + public void Init(byte[] x) + { + this.x = GcmUtilities.AsUints(x); + } - public void ExponentiateX(long pow, byte[] output) - { - // Initial value is little-endian 1 - byte[] y = GcmUtilities.OneAsBytes(); + public void ExponentiateX(long pow, byte[] output) + { + // Initial value is little-endian 1 + uint[] y = GcmUtilities.OneAsUints(); - if (pow > 0) - { - byte[] powX = Arrays.Clone(x); - do - { - if ((pow & 1L) != 0) - { - GcmUtilities.Multiply(y, powX); - } - GcmUtilities.Multiply(powX, powX); - pow >>= 1; - } - while (pow > 0); - } + if (pow > 0) + { + uint[] powX = Arrays.Clone(x); + do + { + if ((pow & 1L) != 0) + { + GcmUtilities.Multiply(y, powX); + } + GcmUtilities.Multiply(powX, powX); + pow >>= 1; + } + while (pow > 0); + } - Array.Copy(y, 0, output, 0, 16); - } - } + GcmUtilities.AsBytes(y, output); + } + } } diff --git a/crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs b/crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
index 85e3ac9b1..eb89383fb 100644 --- a/crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs +++ b/crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
@@ -1,22 +1,22 @@ using System; -using Org.BouncyCastle.Utilities; - namespace Org.BouncyCastle.Crypto.Modes.Gcm { - public class BasicGcmMultiplier - : IGcmMultiplier - { - private byte[] H; + public class BasicGcmMultiplier + : IGcmMultiplier + { + private uint[] H; - public void Init(byte[] H) - { - this.H = Arrays.Clone(H); - } + public void Init(byte[] H) + { + this.H = GcmUtilities.AsUints(H); + } public void MultiplyH(byte[] x) - { - GcmUtilities.Multiply(x, H); - } - } + { + uint[] t = GcmUtilities.AsUints(x); + GcmUtilities.Multiply(t, H); + GcmUtilities.AsBytes(t, x); + } + } } diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
index 71e63c8fd..d8ab2ca73 100644 --- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs +++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -7,6 +7,31 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm { internal abstract class GcmUtilities { + private const uint E1 = 0xe1000000; + private const ulong E1L = (ulong)E1 << 32; + + private static uint[] GenerateLookup() + { + uint[] lookup = new uint[256]; + + for (int c = 0; c < 256; ++c) + { + uint v = 0; + for (int i = 7; i >= 0; --i) + { + if ((c & (1 << i)) != 0) + { + v ^= (E1 >> (7 - i)); + } + } + lookup[c] = v; + } + + return lookup; + } + + private static readonly uint[] LOOKUP = GenerateLookup(); + internal static byte[] OneAsBytes() { byte[] tmp = new byte[16]; @@ -21,6 +46,35 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm return tmp; } + internal static ulong[] OneAsUlongs() + { + ulong[] tmp = new ulong[2]; + tmp[0] = 1UL << 63; + return tmp; + } + + internal static byte[] AsBytes(uint[] x) + { + return Pack.UInt32_To_BE(x); + } + + internal static void AsBytes(uint[] x, byte[] z) + { + Pack.UInt32_To_BE(x, z, 0); + } + + internal static byte[] AsBytes(ulong[] x) + { + byte[] z = new byte[16]; + Pack.UInt64_To_BE(x, z, 0); + return z; + } + + internal static void AsBytes(ulong[] x, byte[] z) + { + Pack.UInt64_To_BE(x, z, 0); + } + internal static uint[] AsUints(byte[] bs) { uint[] output = new uint[4]; @@ -33,56 +87,90 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm Pack.BE_To_UInt32(bs, 0, output); } - internal static void Multiply(byte[] block, byte[] val) + internal static ulong[] AsUlongs(byte[] x) { - byte[] tmp = Arrays.Clone(block); - byte[] c = new byte[16]; + ulong[] z = new ulong[2]; + Pack.BE_To_UInt64(x, 0, z); + return z; + } + + public static void AsUlongs(byte[] x, ulong[] z) + { + Pack.BE_To_UInt64(x, 0, z); + } - for (int i = 0; i < 16; ++i) + internal static void Multiply(byte[] x, byte[] y) + { + uint[] t1 = GcmUtilities.AsUints(x); + uint[] t2 = GcmUtilities.AsUints(y); + GcmUtilities.Multiply(t1, t2); + GcmUtilities.AsBytes(t1, x); + } + + internal static void Multiply(uint[] x, uint[] y) + { + uint r00 = x[0], r01 = x[1], r02 = x[2], r03 = x[3]; + uint r10 = 0, r11 = 0, r12 = 0, r13 = 0; + + for (int i = 0; i < 4; ++i) { - byte bits = val[i]; - for (int j = 7; j >= 0; --j) + int bits = (int)y[i]; + for (int j = 0; j < 32; ++j) { - if ((bits & (1 << j)) != 0) - { - Xor(c, tmp); - } + uint m1 = (uint)(bits >> 31); bits <<= 1; + r10 ^= (r00 & m1); + r11 ^= (r01 & m1); + r12 ^= (r02 & m1); + r13 ^= (r03 & m1); - bool lsb = (tmp[15] & 1) != 0; - ShiftRight(tmp); - if (lsb) - { - // R = new byte[]{ 0xe1, ... }; - //GCMUtilities.Xor(tmp, R); - tmp[0] ^= (byte)0xe1; - } + uint m2 = (uint)((int)(r03 << 31) >> 8); + r03 = (r03 >> 1) | (r02 << 31); + r02 = (r02 >> 1) | (r01 << 31); + r01 = (r01 >> 1) | (r00 << 31); + r00 = (r00 >> 1) ^ (m2 & E1); } } - Array.Copy(c, 0, block, 0, 16); + x[0] = r10; + x[1] = r11; + x[2] = r12; + x[3] = r13; } - // P is the value with only bit i=1 set - internal static void MultiplyP(uint[] x) + internal static void Multiply(ulong[] x, ulong[] y) { - bool lsb = (x[3] & 1) != 0; - ShiftRight(x); - if (lsb) + ulong r00 = x[0], r01 = x[1], r10 = 0, r11 = 0; + + for (int i = 0; i < 2; ++i) { - // R = new uint[]{ 0xe1000000, 0, 0, 0 }; - //Xor(v, R); - x[0] ^= 0xe1000000; + long bits = (long)y[i]; + for (int j = 0; j < 64; ++j) + { + ulong m1 = (ulong)(bits >> 63); bits <<= 1; + r10 ^= (r00 & m1); + r11 ^= (r01 & m1); + + ulong m2 = (ulong)((long)(r01 << 63) >> 8); + r01 = (r01 >> 1) | (r00 << 63); + r00 = (r00 >> 1) ^ (m2 & E1L); + } } + + x[0] = r10; + x[1] = r11; } - internal static void MultiplyP(uint[] x, uint[] output) + // P is the value with only bit i=1 set + internal static void MultiplyP(uint[] x) { - bool lsb = (x[3] & 1) != 0; - ShiftRight(x, output); - if (lsb) - { - output[0] ^= 0xe1000000; - } + uint m = (uint)((int)ShiftRight(x) >> 8); + x[0] ^= (m & E1); + } + + internal static void MultiplyP(uint[] x, uint[] z) + { + uint m = (uint)((int)ShiftRight(x, z) >> 8); + z[0] ^= (m & E1); } internal static void MultiplyP8(uint[] x) @@ -92,146 +180,140 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm // MultiplyP(x); // } - uint lsw = x[3]; - ShiftRightN(x, 8); - for (int i = 7; i >= 0; --i) - { - if ((lsw & (1 << i)) != 0) - { - x[0] ^= (0xe1000000 >> (7 - i)); - } - } + uint c = ShiftRightN(x, 8); + x[0] ^= LOOKUP[c >> 24]; } - internal static void MultiplyP8(uint[] x, uint[] output) + internal static void MultiplyP8(uint[] x, uint[] y) { - uint lsw = x[3]; - ShiftRightN(x, 8, output); - for (int i = 7; i >= 0; --i) - { - if ((lsw & (1 << i)) != 0) - { - output[0] ^= (0xe1000000 >> (7 - i)); - } - } + uint c = ShiftRightN(x, 8, y); + y[0] ^= LOOKUP[c >> 24]; } - internal static void ShiftRight(byte[] block) + internal static uint ShiftRight(uint[] x) { - int i = 0; - byte bit = 0; - for (; ; ) - { - byte b = block[i]; - block[i] = (byte)((b >> 1) | bit); - if (++i == 16) break; - bit = (byte)(b << 7); - } + uint b = x[0]; + x[0] = b >> 1; + uint c = b << 31; + b = x[1]; + x[1] = (b >> 1) | c; + c = b << 31; + b = x[2]; + x[2] = (b >> 1) | c; + c = b << 31; + b = x[3]; + x[3] = (b >> 1) | c; + return b << 31; } - static void ShiftRight(byte[] block, byte[] output) + internal static uint ShiftRight(uint[] x, uint[] z) { - int i = 0; - byte bit = 0; - for (;;) - { - byte b = block[i]; - output[i] = (byte)((b >> 1) | bit); - if (++i == 16) break; - bit = (byte)(b << 7); - } + uint b = x[0]; + z[0] = b >> 1; + uint c = b << 31; + b = x[1]; + z[1] = (b >> 1) | c; + c = b << 31; + b = x[2]; + z[2] = (b >> 1) | c; + c = b << 31; + b = x[3]; + z[3] = (b >> 1) | c; + return b << 31; } - internal static void ShiftRight(uint[] block) + internal static uint ShiftRightN(uint[] x, int n) { - int i = 0; - uint bit = 0; - for (; ; ) - { - uint b = block[i]; - block[i] = (b >> 1) | bit; - if (++i == 4) break; - bit = b << 31; - } + uint b = x[0]; int nInv = 32 - n; + x[0] = b >> n; + uint c = b << nInv; + b = x[1]; + x[1] = (b >> n) | c; + c = b << nInv; + b = x[2]; + x[2] = (b >> n) | c; + c = b << nInv; + b = x[3]; + x[3] = (b >> n) | c; + return b << nInv; } - internal static void ShiftRight(uint[] block, uint[] output) + internal static uint ShiftRightN(uint[] x, int n, uint[] z) { - int i = 0; - uint bit = 0; - for (; ; ) - { - uint b = block[i]; - output[i] = (b >> 1) | bit; - if (++i == 4) break; - bit = b << 31; - } + uint b = x[0]; int nInv = 32 - n; + z[0] = b >> n; + uint c = b << nInv; + b = x[1]; + z[1] = (b >> n) | c; + c = b << nInv; + b = x[2]; + z[2] = (b >> n) | c; + c = b << nInv; + b = x[3]; + z[3] = (b >> n) | c; + return b << nInv; } - internal static void ShiftRightN(uint[] block, int n) + internal static void Xor(byte[] x, byte[] y) { int i = 0; - uint bit = 0; - for (; ; ) + do { - uint b = block[i]; - block[i] = (b >> n) | bit; - if (++i == 4) break; - bit = b << (32 - n); + x[i] ^= y[i]; ++i; + x[i] ^= y[i]; ++i; + x[i] ^= y[i]; ++i; + x[i] ^= y[i]; ++i; } + while (i < 16); } - internal static void ShiftRightN(uint[] block, int n, uint[] output) + internal static void Xor(byte[] x, byte[] y, int yOff, int yLen) { - int i = 0; - uint bit = 0; - for (; ; ) + while (--yLen >= 0) { - uint b = block[i]; - output[i] = (b >> n) | bit; - if (++i == 4) break; - bit = b << (32 - n); + x[yLen] ^= y[yOff + yLen]; } } - internal static void Xor(byte[] block, byte[] val) + internal static void Xor(byte[] x, byte[] y, byte[] z) { - for (int i = 15; i >= 0; --i) + int i = 0; + do { - block[i] ^= val[i]; + z[i] = (byte)(x[i] ^ y[i]); ++i; + z[i] = (byte)(x[i] ^ y[i]); ++i; + z[i] = (byte)(x[i] ^ y[i]); ++i; + z[i] = (byte)(x[i] ^ y[i]); ++i; } + while (i < 16); } - internal static void Xor(byte[] block, byte[] val, int off, int len) + internal static void Xor(uint[] x, uint[] y) { - while (--len >= 0) - { - block[len] ^= val[off + len]; - } + x[0] ^= y[0]; + x[1] ^= y[1]; + x[2] ^= y[2]; + x[3] ^= y[3]; } - internal static void Xor(byte[] block, byte[] val, byte[] output) + internal static void Xor(uint[] x, uint[] y, uint[] z) { - for (int i = 15; i >= 0; --i) - { - output[i] = (byte)(block[i] ^ val[i]); - } + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; } - internal static void Xor(uint[] block, uint[] val) + internal static void Xor(ulong[] x, ulong[] y) { - for (int i = 3; i >= 0; --i) - { - block[i] ^= val[i]; - } + x[0] ^= y[0]; + x[1] ^= y[1]; } - internal static void Xor(uint[] block, uint[] val, uint[] output) + internal static void Xor(ulong[] x, ulong[] y, ulong[] z) { - for (int i = 3; i >= 0; --i) - { - output[i] = block[i] ^ val[i]; - } + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; } } } diff --git a/crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs b/crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
index 44933bba7..e649d6770 100644 --- a/crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs +++ b/crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
@@ -5,48 +5,47 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes.Gcm { - public class Tables1kGcmExponentiator - : IGcmExponentiator - { + public class Tables1kGcmExponentiator + : IGcmExponentiator + { // A lookup table of the power-of-two powers of 'x' // - lookupPowX2[i] = x^(2^i) private IList lookupPowX2; public void Init(byte[] x) - { - if (lookupPowX2 != null && Arrays.AreEqual(x, (byte[])lookupPowX2[0])) - { + { + uint[] y = GcmUtilities.AsUints(x); + if (lookupPowX2 != null && Arrays.AreEqual(y, (uint[])lookupPowX2[0])) return; - } lookupPowX2 = Platform.CreateArrayList(8); - lookupPowX2.Add(Arrays.Clone(x)); - } + lookupPowX2.Add(y); + } - public void ExponentiateX(long pow, byte[] output) - { - byte[] y = GcmUtilities.OneAsBytes(); + public void ExponentiateX(long pow, byte[] output) + { + uint[] y = GcmUtilities.OneAsUints(); int bit = 0; while (pow > 0) { if ((pow & 1L) != 0) { EnsureAvailable(bit); - GcmUtilities.Multiply(y, (byte[])lookupPowX2[bit]); + GcmUtilities.Multiply(y, (uint[])lookupPowX2[bit]); } ++bit; pow >>= 1; } - Array.Copy(y, 0, output, 0, 16); - } + GcmUtilities.AsBytes(y, output); + } private void EnsureAvailable(int bit) { int count = lookupPowX2.Count; if (count <= bit) { - byte[] tmp = (byte[])lookupPowX2[count - 1]; + uint[] tmp = (uint[])lookupPowX2[count - 1]; do { tmp = Arrays.Clone(tmp); diff --git a/crypto/src/crypto/operators/Asn1Signature.cs b/crypto/src/crypto/operators/Asn1Signature.cs new file mode 100644
index 000000000..ba070f4fc --- /dev/null +++ b/crypto/src/crypto/operators/Asn1Signature.cs
@@ -0,0 +1,559 @@ +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.Asn1.X9; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Crypto.Operators +{ + internal class X509Utilities + { + private static readonly Asn1Null derNull = DerNull.Instance; + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary exParams = Platform.CreateHashtable(); + 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("SHA384WITHDSA", NistObjectIdentifiers.DsaWithSha384); + algorithms.Add("SHA512WITHDSA", NistObjectIdentifiers.DsaWithSha512); + 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); + noParams.Add(NistObjectIdentifiers.DsaWithSha384); + noParams.Add(NistObjectIdentifiers.DsaWithSha512); + + // + // 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)); + } + + /** + * 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; + } + } + + 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; + } + + 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 = Platform.ToUpperInvariant(algorithmName); + + if (algorithms.Contains(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 = Platform.ToUpperInvariant(algorithmName); + + if (exParams.Contains(algorithmName)) + { + return new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable GetAlgNames() + { + return new EnumerableProxy(algorithms.Keys); + } + } + + internal class SignerBucket + : Stream + { + protected readonly ISigner signer; + + public SignerBucket( + ISigner signer) + { + this.signer = signer; + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + throw new NotImplementedException (); + } + + public override int ReadByte() + { + throw new NotImplementedException (); + } + + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (count > 0) + { + signer.BlockUpdate(buffer, offset, count); + } + } + + public override void WriteByte( + byte b) + { + signer.Update(b); + } + + public override bool CanRead + { + get { return false; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override long Length + { + get { return 0; } + } + + public override long Position + { + get { throw new NotImplementedException (); } + set { throw new NotImplementedException (); } + } + + public override void Close() + { + } + + public override void Flush() + { + } + + public override long Seek( + long offset, + SeekOrigin origin) + { + throw new NotImplementedException (); + } + + public override void SetLength( + long length) + { + throw new NotImplementedException (); + } + } + + /// <summary> + /// Calculator class for signature generation in ASN.1 based profiles that use an AlgorithmIdentifier to preserve + /// signature algorithm details. + /// </summary> + public class Asn1SignatureCalculator: ISignatureCalculator + { + private readonly AlgorithmIdentifier algID; + private readonly string algorithm; + private readonly AsymmetricKeyParameter privateKey; + private readonly SecureRandom random; + + /// <summary> + /// Base constructor. + /// </summary> + /// <param name="algorithm">The name of the signature algorithm to use.</param> + /// <param name="privateKey">The private key to be used in the signing operation.</param> + public Asn1SignatureCalculator (string algorithm, AsymmetricKeyParameter privateKey): this(algorithm, privateKey, null) + { + } + + /// <summary> + /// Constructor which also specifies a source of randomness to be used if one is required. + /// </summary> + /// <param name="algorithm">The name of the signature algorithm to use.</param> + /// <param name="privateKey">The private key to be used in the signing operation.</param> + /// <param name="random">The source of randomness to be used in signature calculation.</param> + public Asn1SignatureCalculator (string algorithm, AsymmetricKeyParameter privateKey, SecureRandom random) + { + DerObjectIdentifier sigOid = X509Utilities.GetAlgorithmOid (algorithm); + + this.algorithm = algorithm; + this.privateKey = privateKey; + this.random = random; + this.algID = X509Utilities.GetSigAlgID (sigOid, algorithm); + } + + public Object AlgorithmDetails + { + get { return this.algID; } + } + + public IStreamCalculator CreateCalculator() + { + ISigner sig = SignerUtilities.GetSigner(algorithm); + + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + + return new SigCalculator(sig); + } + + /// <summary> + /// Allows enumeration of the signature names supported by the verifier provider. + /// </summary> + public static IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } + + internal class SigCalculator : IStreamCalculator + { + private readonly ISigner sig; + private readonly Stream stream; + + internal SigCalculator(ISigner sig) + { + this.sig = sig; + this.stream = new SignerBucket(sig); + } + + public Stream Stream + { + get { return stream; } + } + + public object GetResult() + { + return new SigResult(sig); + } + } + + internal class SigResult : IBlockResult + { + private readonly ISigner sig; + + internal SigResult(ISigner sig) + { + this.sig = sig; + } + + public byte[] DoFinal() + { + return sig.GenerateSignature(); + } + + public int DoFinal(byte[] destination, int offset) + { + byte[] signature = DoFinal(); + + Array.Copy(signature, 0, destination, offset, signature.Length); + + return signature.Length; + } + } + + /// <summary> + /// Verifier class for signature verification in ASN.1 based profiles that use an AlgorithmIdentifier to preserve + /// signature algorithm details. + /// </summary> + public class Asn1SignatureVerifier: ISignatureVerifier + { + private readonly AlgorithmIdentifier algID; + private readonly AsymmetricKeyParameter publicKey; + + /// <summary> + /// Base constructor. + /// </summary> + /// <param name="algorithm">The name of the signature algorithm to use.</param> + /// <param name="publicKey">The public key to be used in the verification operation.</param> + public Asn1SignatureVerifier (String algorithm, AsymmetricKeyParameter publicKey) + { + DerObjectIdentifier sigOid = X509Utilities.GetAlgorithmOid (algorithm); + + this.publicKey = publicKey; + this.algID = X509Utilities.GetSigAlgID (sigOid, algorithm); + } + + public Asn1SignatureVerifier (AlgorithmIdentifier algorithm, AsymmetricKeyParameter publicKey) + { + this.publicKey = publicKey; + this.algID = algorithm; + } + + public Object AlgorithmDetails + { + get { return this.algID; } + } + + public IStreamCalculator CreateCalculator() + { + ISigner sig = SignerUtilities.GetSigner(X509Utilities.GetSignatureName(algID)); + + sig.Init(false, publicKey); + + return new VerifierCalculator(sig); + } + } + + internal class VerifierCalculator : IStreamCalculator + { + private readonly ISigner sig; + private readonly Stream stream; + + internal VerifierCalculator(ISigner sig) + { + this.sig = sig; + this.stream = new SignerBucket(sig); + } + + public Stream Stream + { + get { return stream; } + } + + public object GetResult() + { + return new VerifierResult(sig); + } + } + + internal class VerifierResult : IVerifier + { + private readonly ISigner sig; + + internal VerifierResult(ISigner sig) + { + this.sig = sig; + } + + public bool IsVerified(byte[] signature) + { + return sig.VerifySignature(signature); + } + + public bool IsVerified(byte[] signature, int off, int length) + { + byte[] sigBytes = new byte[length]; + + Array.Copy(signature, 0, sigBytes, off, sigBytes.Length); + + return sig.VerifySignature(signature); + } + } + + /// <summary> + /// Provider class which supports dynamic creation of signature verifiers. + /// </summary> + public class Asn1SignatureVerifierProvider: ISignatureVerifierProvider + { + private readonly AsymmetricKeyParameter publicKey; + + /// <summary> + /// Base constructor - specify the public key to be used in verification. + /// </summary> + /// <param name="publicKey">The public key to be used in creating verifiers provided by this object.</param> + public Asn1SignatureVerifierProvider(AsymmetricKeyParameter publicKey) + { + this.publicKey = publicKey; + } + + public ISignatureVerifier CreateSignatureVerifier(Object algorithmDetails) + { + return new Asn1SignatureVerifier ((AlgorithmIdentifier)algorithmDetails, publicKey); + } + + /// <summary> + /// Allows enumeration of the signature names supported by the verifier provider. + /// </summary> + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} + diff --git a/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs b/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
index fb8a92ba3..5d2f8cf15 100644 --- a/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs +++ b/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Paddings } /** - * process a single byte, producing an output block if neccessary. + * process a single byte, producing an output block if necessary. * * @param in the input byte. * @param out the space for any output that might be produced. @@ -178,10 +178,7 @@ namespace Org.BouncyCastle.Crypto.Paddings if (outLength > 0) { - if ((outOff + outLength) > output.Length) - { - throw new DataLengthException("output buffer too short"); - } + Check.OutputLength(output, outOff, outLength, "output buffer too short"); } int resultLen = 0; @@ -242,7 +239,7 @@ namespace Org.BouncyCastle.Crypto.Paddings { Reset(); - throw new DataLengthException("output buffer too short"); + throw new OutputLengthException("output buffer too short"); } resultLen = cipher.ProcessBlock(buf, 0, output, outOff); diff --git a/crypto/src/crypto/paddings/Pkcs7Padding.cs b/crypto/src/crypto/paddings/Pkcs7Padding.cs
index f3166fd96..11585647a 100644 --- a/crypto/src/crypto/paddings/Pkcs7Padding.cs +++ b/crypto/src/crypto/paddings/Pkcs7Padding.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Paddings * A padder that adds Pkcs7/Pkcs5 padding to a block. */ public class Pkcs7Padding - : IBlockCipherPadding + : IBlockCipherPadding { /** * Initialise the padder. @@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Paddings * @param random - a SecureRandom if available. */ public void Init( - SecureRandom random) + SecureRandom random) { // nothing to do. } @@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Paddings get { return "PKCS7"; } } - /** + /** * add the pad bytes to the passed in block, returning the * number of bytes added. */ @@ -55,21 +55,18 @@ namespace Org.BouncyCastle.Crypto.Paddings * return the number of pad bytes present in the block. */ public int PadCount( - byte[] input) + byte[] input) { - int count = (int) input[input.Length - 1]; + byte countAsByte = input[input.Length - 1]; + int count = countAsByte; - if (count < 1 || count > input.Length) - { + if (count < 1 || count > input.Length) throw new InvalidCipherTextException("pad block corrupted"); - } - for (int i = 1; i <= count; i++) + for (int i = 2; i <= count; i++) { - if (input[input.Length - i] != count) - { + if (input[input.Length - i] != countAsByte) throw new InvalidCipherTextException("pad block corrupted"); - } } return count; diff --git a/crypto/src/crypto/parameters/DHParameters.cs b/crypto/src/crypto/parameters/DHParameters.cs
index a0544e73b..4258df5c5 100644 --- a/crypto/src/crypto/parameters/DHParameters.cs +++ b/crypto/src/crypto/parameters/DHParameters.cs
@@ -91,6 +91,7 @@ namespace Org.BouncyCastle.Crypto.Parameters throw new ArgumentException("m value must be < bitlength of p", "m"); if (l != 0) { + // TODO Check this against the Java version, which has 'l > p.BitLength' here if (l >= p.BitLength) throw new ArgumentException("when l value specified, it must be less than bitlength(p)", "l"); if (l < m) diff --git a/crypto/src/crypto/parameters/Srp6GroupParameters.cs b/crypto/src/crypto/parameters/Srp6GroupParameters.cs new file mode 100644
index 000000000..6762dd31d --- /dev/null +++ b/crypto/src/crypto/parameters/Srp6GroupParameters.cs
@@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class Srp6GroupParameters + { + private readonly BigInteger n, g; + + public Srp6GroupParameters(BigInteger N, BigInteger g) + { + this.n = N; + this.g = g; + } + + public BigInteger G + { + get { return g; } + } + + public BigInteger N + { + get { return n; } + } + } +} diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index e1a0d4012..521fae33e 100644 --- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs +++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -5,62 +5,62 @@ using System.Security.Cryptography; namespace Org.BouncyCastle.Crypto.Prng { - /// <summary> - /// Uses Microsoft's RNGCryptoServiceProvider - /// </summary> - public class CryptoApiRandomGenerator - : IRandomGenerator - { - private readonly RandomNumberGenerator rndProv; + /// <summary> + /// Uses Microsoft's RNGCryptoServiceProvider + /// </summary> + public class CryptoApiRandomGenerator + : IRandomGenerator + { + private readonly RandomNumberGenerator rndProv; - public CryptoApiRandomGenerator() - : this(new RNGCryptoServiceProvider()) - { - } + public CryptoApiRandomGenerator() + : this(new RNGCryptoServiceProvider()) + { + } - public CryptoApiRandomGenerator(RandomNumberGenerator rng) - { - this.rndProv = rng; - } + public CryptoApiRandomGenerator(RandomNumberGenerator rng) + { + this.rndProv = rng; + } - #region IRandomGenerator Members + #region IRandomGenerator Members - public virtual void AddSeedMaterial(byte[] seed) - { - // We don't care about the seed - } + 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 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) + { + 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"); + 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); - } - } + if (bytes.Length == len && start == 0) + { + NextBytes(bytes); + } + else + { + byte[] tmpBuf = new byte[len]; + NextBytes(tmpBuf); + Array.Copy(tmpBuf, 0, bytes, start, len); + } + } - #endregion - } + #endregion + } } #endif diff --git a/crypto/src/crypto/signers/DsaDigestSigner.cs b/crypto/src/crypto/signers/DsaDigestSigner.cs
index aee713450..086601481 100644 --- a/crypto/src/crypto/signers/DsaDigestSigner.cs +++ b/crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -26,12 +26,12 @@ namespace Org.BouncyCastle.Crypto.Signers this.dsaSigner = signer; } - public string AlgorithmName + public virtual string AlgorithmName { get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } } - public void Init( + public virtual void Init( bool forSigning, ICipherParameters parameters) { @@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers /** * update the internal digest with the byte b */ - public void Update( + public virtual void Update( byte input) { digest.Update(input); @@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Signers /** * update the internal digest with the byte array in */ - public void BlockUpdate( + public virtual void BlockUpdate( byte[] input, int inOff, int length) @@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Signers * Generate a signature for the message we've been loaded with using * the key we were initialised with. */ - public byte[] GenerateSignature() + public virtual byte[] GenerateSignature() { if (!forSigning) throw new InvalidOperationException("DSADigestSigner not initialised for signature generation."); @@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Crypto.Signers } /// <returns>true if the internal state represents the signature described in the passed in array.</returns> - public bool VerifySignature( + public virtual bool VerifySignature( byte[] signature) { if (forSigning) @@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Crypto.Signers } /// <summary>Reset the internal state</summary> - public void Reset() + public virtual void Reset() { digest.Reset(); } diff --git a/crypto/src/crypto/signers/DsaSigner.cs b/crypto/src/crypto/signers/DsaSigner.cs
index fc0780a91..bb28addfc 100644 --- a/crypto/src/crypto/signers/DsaSigner.cs +++ b/crypto/src/crypto/signers/DsaSigner.cs
@@ -1,10 +1,9 @@ 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; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Signers { @@ -15,9 +14,29 @@ namespace Org.BouncyCastle.Crypto.Signers public class DsaSigner : IDsa { + protected readonly IDsaKCalculator kCalculator; + protected DsaKeyParameters key = null; protected SecureRandom random = null; + /** + * Default configuration, random K values. + */ + public DsaSigner() + { + this.kCalculator = new RandomDsaKCalculator(); + } + + /** + * Configuration with an alternate, possibly deterministic calculator of K. + * + * @param kCalculator a K value calculator. + */ + public DsaSigner(IDsaKCalculator kCalculator) + { + this.kCalculator = kCalculator; + } + public virtual string AlgorithmName { get { return "DSA"; } @@ -50,7 +69,7 @@ namespace Org.BouncyCastle.Crypto.Signers this.key = (DsaPublicKeyParameters)parameters; } - this.random = InitSecureRandom(forSigning, providedRandom); + this.random = InitSecureRandom(forSigning && !kCalculator.IsDeterministic, providedRandom); } /** @@ -65,18 +84,22 @@ namespace Org.BouncyCastle.Crypto.Signers DsaParameters parameters = key.Parameters; BigInteger q = parameters.Q; BigInteger m = CalculateE(q, message); - BigInteger k; + BigInteger x = ((DsaPrivateKeyParameters)key).X; - do + if (kCalculator.IsDeterministic) { - k = new BigInteger(q.BitLength, random); + kCalculator.Init(q, x, message); } - while (k.CompareTo(q) >= 0); + else + { + kCalculator.Init(q, random); + } + + BigInteger k = kCalculator.NextK(); BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q); - k = k.ModInverse(q).Multiply( - m.Add(((DsaPrivateKeyParameters)key).X.Multiply(r))); + k = k.ModInverse(q).Multiply(m.Add(x.Multiply(r))); BigInteger s = k.Mod(q); diff --git a/crypto/src/crypto/signers/ECDsaSigner.cs b/crypto/src/crypto/signers/ECDsaSigner.cs
index 867520535..9821732c2 100644 --- a/crypto/src/crypto/signers/ECDsaSigner.cs +++ b/crypto/src/crypto/signers/ECDsaSigner.cs
@@ -1,11 +1,11 @@ using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.EC.Multiplier; using Org.BouncyCastle.Security; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Signers { @@ -15,9 +15,29 @@ namespace Org.BouncyCastle.Crypto.Signers public class ECDsaSigner : IDsa { + protected readonly IDsaKCalculator kCalculator; + protected ECKeyParameters key = null; protected SecureRandom random = null; + /** + * Default configuration, random K values. + */ + public ECDsaSigner() + { + this.kCalculator = new RandomDsaKCalculator(); + } + + /** + * Configuration with an alternate, possibly deterministic calculator of K. + * + * @param kCalculator a K value calculator. + */ + public ECDsaSigner(IDsaKCalculator kCalculator) + { + this.kCalculator = kCalculator; + } + public virtual string AlgorithmName { get { return "ECDSA"; } @@ -50,7 +70,7 @@ namespace Org.BouncyCastle.Crypto.Signers this.key = (ECPublicKeyParameters)parameters; } - this.random = InitSecureRandom(forSigning, providedRandom); + this.random = InitSecureRandom(forSigning && !kCalculator.IsDeterministic, providedRandom); } // 5.3 pg 28 @@ -68,6 +88,15 @@ namespace Org.BouncyCastle.Crypto.Signers BigInteger e = CalculateE(n, message); BigInteger d = ((ECPrivateKeyParameters)key).D; + if (kCalculator.IsDeterministic) + { + kCalculator.Init(n, d, message); + } + else + { + kCalculator.Init(n, random); + } + BigInteger r, s; ECMultiplier basePointMultiplier = CreateBasePointMultiplier(); @@ -78,11 +107,7 @@ namespace Org.BouncyCastle.Crypto.Signers BigInteger k; do // Generate r { - do - { - k = new BigInteger(n.BitLength, random); - } - while (k.SignValue == 0 || k.CompareTo(n) >= 0); + k = kCalculator.NextK(); ECPoint p = basePointMultiplier.Multiply(ec.G, k).Normalize(); diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs
index 6027aa9b9..28ab79c1c 100644 --- a/crypto/src/crypto/signers/ECGOST3410Signer.cs +++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -18,12 +18,12 @@ namespace Org.BouncyCastle.Crypto.Signers private ECKeyParameters key; private SecureRandom random; - public string AlgorithmName + public virtual string AlgorithmName { get { return "ECGOST3410"; } } - public void Init( + public virtual void Init( bool forSigning, ICipherParameters parameters) { @@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers * * @param message the message that will be verified later. */ - public BigInteger[] GenerateSignature( + public virtual BigInteger[] GenerateSignature( byte[] message) { byte[] mRev = new byte[message.Length]; // conversion is little-endian @@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Signers * the passed in message (for standard GOST3410 the message should be * a GOST3411 hash of the real message to be verified). */ - public bool VerifySignature( + public virtual bool VerifySignature( byte[] message, BigInteger r, BigInteger s) diff --git a/crypto/src/crypto/signers/ECNRSigner.cs b/crypto/src/crypto/signers/ECNRSigner.cs
index cae15bdbf..bb21a4994 100644 --- a/crypto/src/crypto/signers/ECNRSigner.cs +++ b/crypto/src/crypto/signers/ECNRSigner.cs
@@ -19,12 +19,12 @@ namespace Org.BouncyCastle.Crypto.Signers private ECKeyParameters key; private SecureRandom random; - public string AlgorithmName + public virtual string AlgorithmName { get { return "ECNR"; } } - public void Init( + public virtual void Init( bool forSigning, ICipherParameters parameters) { @@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Crypto.Signers * @param digest the digest to be signed. * @exception DataLengthException if the digest is longer than the key allows */ - public BigInteger[] GenerateSignature( + public virtual BigInteger[] GenerateSignature( byte[] message) { if (!this.forSigning) @@ -134,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Signers * @param s the s value of the signature. * @exception DataLengthException if the digest is longer than the key allows */ - public bool VerifySignature( + public virtual bool VerifySignature( byte[] message, BigInteger r, BigInteger s) diff --git a/crypto/src/crypto/signers/GOST3410DigestSigner.cs b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
index 58aefa368..bc32808df 100644 --- a/crypto/src/crypto/signers/GOST3410DigestSigner.cs +++ b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -26,12 +26,12 @@ namespace Org.BouncyCastle.Crypto.Signers this.digest = digest; } - public string AlgorithmName + public virtual string AlgorithmName { get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } } - public void Init( + public virtual void Init( bool forSigning, ICipherParameters parameters) { @@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Crypto.Signers /** * update the internal digest with the byte b */ - public void Update( + public virtual void Update( byte input) { digest.Update(input); @@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Crypto.Signers /** * update the internal digest with the byte array in */ - public void BlockUpdate( + public virtual void BlockUpdate( byte[] input, int inOff, int length) @@ -86,7 +86,7 @@ namespace Org.BouncyCastle.Crypto.Signers * Generate a signature for the message we've been loaded with using * the key we were initialised with. */ - public byte[] GenerateSignature() + public virtual byte[] GenerateSignature() { if (!forSigning) throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation."); @@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Signers } /// <returns>true if the internal state represents the signature described in the passed in array.</returns> - public bool VerifySignature( + public virtual bool VerifySignature( byte[] signature) { if (forSigning) @@ -137,7 +137,7 @@ namespace Org.BouncyCastle.Crypto.Signers } /// <summary>Reset the internal state</summary> - public void Reset() + public virtual void Reset() { digest.Reset(); } diff --git a/crypto/src/crypto/signers/GOST3410Signer.cs b/crypto/src/crypto/signers/GOST3410Signer.cs
index 375eeb5cc..f1832ae37 100644 --- a/crypto/src/crypto/signers/GOST3410Signer.cs +++ b/crypto/src/crypto/signers/GOST3410Signer.cs
@@ -15,12 +15,12 @@ namespace Org.BouncyCastle.Crypto.Signers private Gost3410KeyParameters key; private SecureRandom random; - public string AlgorithmName + public virtual string AlgorithmName { get { return "GOST3410"; } } - public void Init( + public virtual void Init( bool forSigning, ICipherParameters parameters) { @@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Crypto.Signers * * @param message the message that will be verified later. */ - public BigInteger[] GenerateSignature( + public virtual BigInteger[] GenerateSignature( byte[] message) { byte[] mRev = new byte[message.Length]; // conversion is little-endian @@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Signers * the passed in message for standard Gost3410 the message should be a * Gost3411 hash of the real message to be verified. */ - public bool VerifySignature( + public virtual bool VerifySignature( byte[] message, BigInteger r, BigInteger s) diff --git a/crypto/src/crypto/signers/GenericSigner.cs b/crypto/src/crypto/signers/GenericSigner.cs
index 1a53eee2b..a5512176f 100644 --- a/crypto/src/crypto/signers/GenericSigner.cs +++ b/crypto/src/crypto/signers/GenericSigner.cs
@@ -6,124 +6,125 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Signers { - public class GenericSigner - : ISigner - { - private readonly IAsymmetricBlockCipher engine; - private readonly IDigest digest; - private bool forSigning; - - public GenericSigner( - IAsymmetricBlockCipher engine, - IDigest digest) - { - this.engine = engine; - this.digest = digest; - } - - public string AlgorithmName - { - get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; } - } - - /** - * initialise the signer for signing or verification. - * - * @param forSigning - * true if for signing, false otherwise - * @param parameters - * 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; - } + public class GenericSigner + : ISigner + { + private readonly IAsymmetricBlockCipher engine; + private readonly IDigest digest; + private bool forSigning; + + public GenericSigner( + IAsymmetricBlockCipher engine, + IDigest digest) + { + this.engine = engine; + this.digest = digest; + } + + public virtual string AlgorithmName + { + get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; } + } + + /** + * initialise the signer for signing or verification. + * + * @param forSigning + * true if for signing, false otherwise + * @param parameters + * necessary parameters. + */ + public virtual 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) + if (!forSigning && k.IsPrivate) throw new InvalidKeyException("Verification requires public key."); - Reset(); - - engine.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("GenericSigner not initialised for signature generation."); - - byte[] hash = new byte[digest.GetDigestSize()]; - digest.DoFinal(hash, 0); - - return engine.ProcessBlock(hash, 0, hash.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("GenericSigner not initialised for verification"); - - byte[] hash = new byte[digest.GetDigestSize()]; - digest.DoFinal(hash, 0); - - try - { - byte[] sig = engine.ProcessBlock(signature, 0, signature.Length); - - return Arrays.ConstantTimeAreEqual(sig, hash); - } - catch (Exception) - { - return false; - } - } - - public void Reset() - { - digest.Reset(); - } - } + Reset(); + + engine.Init(forSigning, parameters); + } + + /** + * 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); + } + + /** + * Generate a signature for the message we've been loaded with using the key + * we were initialised with. + */ + public virtual byte[] GenerateSignature() + { + if (!forSigning) + throw new InvalidOperationException("GenericSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + return engine.ProcessBlock(hash, 0, hash.Length); + } + + /** + * return true if the internal state represents the signature described in + * the passed in array. + */ + public virtual bool VerifySignature(byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("GenericSigner not initialised for verification"); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + try + { + byte[] sig = engine.ProcessBlock(signature, 0, signature.Length); + + // Extend with leading zeroes to match the digest size, if necessary. + if (sig.Length < hash.Length) + { + byte[] tmp = new byte[hash.Length]; + Array.Copy(sig, 0, tmp, tmp.Length - sig.Length, sig.Length); + sig = tmp; + } + + return Arrays.ConstantTimeAreEqual(sig, hash); + } + catch (Exception) + { + return false; + } + } + + public virtual void Reset() + { + digest.Reset(); + } + } } diff --git a/crypto/src/crypto/signers/HMacDsaKCalculator.cs b/crypto/src/crypto/signers/HMacDsaKCalculator.cs new file mode 100644
index 000000000..8231197b9 --- /dev/null +++ b/crypto/src/crypto/signers/HMacDsaKCalculator.cs
@@ -0,0 +1,150 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979. + */ + public class HMacDsaKCalculator + : IDsaKCalculator + { + private readonly HMac hMac; + private readonly byte[] K; + private readonly byte[] V; + + private BigInteger n; + + /** + * Base constructor. + * + * @param digest digest to build the HMAC on. + */ + public HMacDsaKCalculator(IDigest digest) + { + this.hMac = new HMac(digest); + this.V = new byte[hMac.GetMacSize()]; + this.K = new byte[hMac.GetMacSize()]; + } + + public virtual bool IsDeterministic + { + get { return true; } + } + + public virtual void Init(BigInteger n, SecureRandom random) + { + throw new InvalidOperationException("Operation not supported"); + } + + public void Init(BigInteger n, BigInteger d, byte[] message) + { + this.n = n; + + Arrays.Fill(V, (byte)0x01); + Arrays.Fill(K, (byte)0); + + byte[] x = new byte[(n.BitLength + 7) / 8]; + byte[] dVal = BigIntegers.AsUnsignedByteArray(d); + + Array.Copy(dVal, 0, x, x.Length - dVal.Length, dVal.Length); + + byte[] m = new byte[(n.BitLength + 7) / 8]; + + BigInteger mInt = BitsToInt(message); + + if (mInt.CompareTo(n) >= 0) + { + mInt = mInt.Subtract(n); + } + + byte[] mVal = BigIntegers.AsUnsignedByteArray(mInt); + + Array.Copy(mVal, 0, m, m.Length - mVal.Length, mVal.Length); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + hMac.Update((byte)0x00); + hMac.BlockUpdate(x, 0, x.Length); + hMac.BlockUpdate(m, 0, m.Length); + + hMac.DoFinal(K, 0); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + + hMac.BlockUpdate(V, 0, V.Length); + hMac.Update((byte)0x01); + hMac.BlockUpdate(x, 0, x.Length); + hMac.BlockUpdate(m, 0, m.Length); + + hMac.DoFinal(K, 0); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + } + + public virtual BigInteger NextK() + { + byte[] t = new byte[((n.BitLength + 7) / 8)]; + + for (;;) + { + int tOff = 0; + + while (tOff < t.Length) + { + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + + int len = System.Math.Min(t.Length - tOff, V.Length); + Array.Copy(V, 0, t, tOff, len); + tOff += len; + } + + BigInteger k = BitsToInt(t); + + if (k.SignValue > 0 && k.CompareTo(n) < 0) + { + return k; + } + + hMac.BlockUpdate(V, 0, V.Length); + hMac.Update((byte)0x00); + + hMac.DoFinal(K, 0); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + } + } + + private BigInteger BitsToInt(byte[] t) + { + BigInteger v = new BigInteger(1, t); + + if (t.Length * 8 > n.BitLength) + { + v = v.ShiftRight(t.Length * 8 - n.BitLength); + } + + return v; + } + } +} diff --git a/crypto/src/crypto/signers/IDsaKCalculator.cs b/crypto/src/crypto/signers/IDsaKCalculator.cs new file mode 100644
index 000000000..645186d41 --- /dev/null +++ b/crypto/src/crypto/signers/IDsaKCalculator.cs
@@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * Interface define calculators of K values for DSA/ECDSA. + */ + public interface IDsaKCalculator + { + /** + * Return true if this calculator is deterministic, false otherwise. + * + * @return true if deterministic, otherwise false. + */ + bool IsDeterministic { get; } + + /** + * Non-deterministic initialiser. + * + * @param n the order of the DSA group. + * @param random a source of randomness. + */ + void Init(BigInteger n, SecureRandom random); + + /** + * Deterministic initialiser. + * + * @param n the order of the DSA group. + * @param d the DSA private value. + * @param message the message being signed. + */ + void Init(BigInteger n, BigInteger d, byte[] message); + + /** + * Return the next valid value of K. + * + * @return a K value. + */ + BigInteger NextK(); + } +} diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
index d4f6c5522..fb117c19d 100644 --- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs +++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -26,29 +26,23 @@ namespace Org.BouncyCastle.Crypto.Signers return recoveredMessage; } + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerImplicit = 0xBC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerRipeMD160 = 0x31CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerRipeMD128 = 0x32CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha1 = 0x33CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha256 = 0x34CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha512 = 0x35CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha384 = 0x36CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerWhirlpool = 0x37CC; - private static readonly IDictionary trailerMap = Platform.CreateHashtable(); - - static Iso9796d2PssSigner() - { - trailerMap.Add("RIPEMD128", TrailerRipeMD128); - trailerMap.Add("RIPEMD160", TrailerRipeMD160); - trailerMap.Add("SHA-1", TrailerSha1); - trailerMap.Add("SHA-256", TrailerSha256); - trailerMap.Add("SHA-384", TrailerSha384); - trailerMap.Add("SHA-512", TrailerSha512); - - trailerMap.Add("Whirlpool", TrailerWhirlpool); - } - private IDigest digest; private IAsymmetricBlockCipher cipher; @@ -71,8 +65,7 @@ namespace Org.BouncyCastle.Crypto.Signers private int preTLength; /// <summary> - /// Generate a signer for the with either implicit or explicit trailers - /// for ISO9796-2, scheme 2 or 3. + /// Generate a signer with either implicit or explicit trailers for ISO9796-2, scheme 2 or 3. /// </summary> /// <param name="cipher">base cipher to use for signature creation/verification</param> /// <param name="digest">digest to use.</param> @@ -91,15 +84,15 @@ namespace Org.BouncyCastle.Crypto.Signers if (isImplicit) { - trailer = TrailerImplicit; + trailer = IsoTrailers.TRAILER_IMPLICIT; + } + else if (IsoTrailers.NoTrailerAvailable(digest)) + { + throw new ArgumentException("no valid trailer", "digest"); } else { - string digestAlg = digest.AlgorithmName; - if (!trailerMap.Contains(digestAlg)) - throw new ArgumentException("no valid trailer for digest"); - - trailer = (int)trailerMap[digestAlg]; + trailer = IsoTrailers.GetTrailer(digest); } } @@ -120,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Signers { } - public string AlgorithmName + public virtual string AlgorithmName { get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; } } @@ -180,7 +173,7 @@ namespace Org.BouncyCastle.Crypto.Signers block = new byte[(keyBits + 7) / 8]; - if (trailer == TrailerImplicit) + if (trailer == IsoTrailers.TRAILER_IMPLICIT) { mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1]; } @@ -247,11 +240,10 @@ namespace Org.BouncyCastle.Crypto.Signers { int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF); - string digestAlg = digest.AlgorithmName; - if (!trailerMap.Contains(digestAlg)) + if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("unrecognised hash in signature"); - if (sigTrail != (int)trailerMap[digestAlg]) + if (sigTrail != IsoTrailers.GetTrailer(digest)) throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail); tLength = 2; @@ -365,7 +357,7 @@ namespace Org.BouncyCastle.Crypto.Signers /// <summary> Generate a signature for the loaded message using the key we were /// initialised with. /// </summary> - public byte[] GenerateSignature() + public virtual byte[] GenerateSignature() { int digSize = digest.GetDigestSize(); byte[] m2Hash = new byte[digSize]; @@ -395,7 +387,7 @@ namespace Org.BouncyCastle.Crypto.Signers digest.DoFinal(hash, 0); int tLength = 2; - if (trailer == TrailerImplicit) + if (trailer == IsoTrailers.TRAILER_IMPLICIT) { tLength = 1; } @@ -415,9 +407,9 @@ namespace Org.BouncyCastle.Crypto.Signers Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen); - if (trailer == TrailerImplicit) + if (trailer == IsoTrailers.TRAILER_IMPLICIT) { - block[block.Length - 1] = (byte)TrailerImplicit; + block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT; } else { diff --git a/crypto/src/crypto/signers/Iso9796d2Signer.cs b/crypto/src/crypto/signers/Iso9796d2Signer.cs
index cfb8942e6..b90ed8f0b 100644 --- a/crypto/src/crypto/signers/Iso9796d2Signer.cs +++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -21,30 +21,23 @@ namespace Org.BouncyCastle.Crypto.Signers return recoveredMessage; } + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerImplicit = 0xBC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerRipeMD160 = 0x31CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerRipeMD128 = 0x32CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha1 = 0x33CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha256 = 0x34CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha512 = 0x35CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerSha384 = 0x36CC; + [Obsolete("Use 'IsoTrailers' instead")] public const int TrailerWhirlpool = 0x37CC; - private static IDictionary trailerMap = Platform.CreateHashtable(); - - static Iso9796d2Signer() - { - trailerMap.Add("RIPEMD128", TrailerRipeMD128); - trailerMap.Add("RIPEMD160", TrailerRipeMD160); - - trailerMap.Add("SHA-1", TrailerSha1); - trailerMap.Add("SHA-256", TrailerSha256); - trailerMap.Add("SHA-384", TrailerSha384); - trailerMap.Add("SHA-512", TrailerSha512); - - trailerMap.Add("Whirlpool", TrailerWhirlpool); - } - private IDigest digest; private IAsymmetricBlockCipher cipher; @@ -60,8 +53,7 @@ namespace Org.BouncyCastle.Crypto.Signers private byte[] preBlock; /// <summary> - /// Generate a signer for the with either implicit or explicit trailers - /// for ISO9796-2. + /// Generate a signer with either implicit or explicit trailers for ISO9796-2. /// </summary> /// <param name="cipher">base cipher to use for signature creation/verification</param> /// <param name="digest">digest to use.</param> @@ -76,20 +68,15 @@ namespace Org.BouncyCastle.Crypto.Signers if (isImplicit) { - trailer = TrailerImplicit; + trailer = IsoTrailers.TRAILER_IMPLICIT; + } + else if (IsoTrailers.NoTrailerAvailable(digest)) + { + throw new ArgumentException("no valid trailer", "digest"); } else { - string digestName = digest.AlgorithmName; - - if (trailerMap.Contains(digestName)) - { - trailer = (int)trailerMap[digest.AlgorithmName]; - } - else - { - throw new System.ArgumentException("no valid trailer for digest"); - } + trailer = IsoTrailers.GetTrailer(digest); } } @@ -105,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Signers { } - public string AlgorithmName + public virtual string AlgorithmName { get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; } } @@ -119,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Signers keyBits = kParam.Modulus.BitLength; block = new byte[(keyBits + 7) / 8]; - if (trailer == TrailerImplicit) + if (trailer == IsoTrailers.TRAILER_IMPLICIT) { mBuf = new byte[block.Length - digest.GetDigestSize() - 2]; } @@ -195,10 +182,10 @@ namespace Org.BouncyCastle.Crypto.Signers { int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF); - string digestName = digest.AlgorithmName; - if (!trailerMap.Contains(digestName)) + if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("unrecognised hash in signature"); - if (sigTrail != (int)trailerMap[digestName]) + + if (sigTrail != IsoTrailers.GetTrailer(digest)) throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail); delta = 2; @@ -252,7 +239,7 @@ namespace Org.BouncyCastle.Crypto.Signers } /// <summary> update the internal digest with the byte b</summary> - public void Update( + public virtual void Update( byte input) { digest.Update(input); @@ -266,7 +253,7 @@ namespace Org.BouncyCastle.Crypto.Signers } /// <summary> update the internal digest with the byte array in</summary> - public void BlockUpdate( + public virtual void BlockUpdate( byte[] input, int inOff, int length) @@ -319,12 +306,12 @@ namespace Org.BouncyCastle.Crypto.Signers int t = 0; int delta = 0; - if (trailer == TrailerImplicit) + if (trailer == IsoTrailers.TRAILER_IMPLICIT) { t = 8; delta = block.Length - digSize - 1; digest.DoFinal(block, delta); - block[block.Length - 1] = (byte) TrailerImplicit; + block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT; } else { @@ -424,10 +411,10 @@ namespace Org.BouncyCastle.Crypto.Signers { int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF); - string digestName = digest.AlgorithmName; - if (!trailerMap.Contains(digestName)) + if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("unrecognised hash in signature"); - if (sigTrail != (int)trailerMap[digestName]) + + if (sigTrail != IsoTrailers.GetTrailer(digest)) throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail); delta = 2; diff --git a/crypto/src/crypto/signers/IsoTrailers.cs b/crypto/src/crypto/signers/IsoTrailers.cs new file mode 100644
index 000000000..497ffaf78 --- /dev/null +++ b/crypto/src/crypto/signers/IsoTrailers.cs
@@ -0,0 +1,57 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class IsoTrailers + { + public const int TRAILER_IMPLICIT = 0xBC; + public const int TRAILER_RIPEMD160 = 0x31CC; + public const int TRAILER_RIPEMD128 = 0x32CC; + public const int TRAILER_SHA1 = 0x33CC; + public const int TRAILER_SHA256 = 0x34CC; + public const int TRAILER_SHA512 = 0x35CC; + public const int TRAILER_SHA384 = 0x36CC; + public const int TRAILER_WHIRLPOOL = 0x37CC; + public const int TRAILER_SHA224 = 0x38CC; + public const int TRAILER_SHA512_224 = 0x39CC; + public const int TRAILER_SHA512_256 = 0x40CC; + + private static IDictionary CreateTrailerMap() + { + IDictionary trailers = Platform.CreateHashtable(); + + trailers.Add("RIPEMD128", TRAILER_RIPEMD128); + trailers.Add("RIPEMD160", TRAILER_RIPEMD160); + + trailers.Add("SHA-1", TRAILER_SHA1); + trailers.Add("SHA-224", TRAILER_SHA224); + trailers.Add("SHA-256", TRAILER_SHA256); + trailers.Add("SHA-384", TRAILER_SHA384); + trailers.Add("SHA-512", TRAILER_SHA512); + trailers.Add("SHA-512/224", TRAILER_SHA512_224); + trailers.Add("SHA-512/256", TRAILER_SHA512_256); + + trailers.Add("Whirlpool", TRAILER_WHIRLPOOL); + + return CollectionUtilities.ReadOnly(trailers); + } + + // IDictionary is (string -> Int32) + private static readonly IDictionary trailerMap = CreateTrailerMap(); + + public static int GetTrailer(IDigest digest) + { + return (int)trailerMap[digest.AlgorithmName]; + } + + public static bool NoTrailerAvailable(IDigest digest) + { + return !trailerMap.Contains(digest.AlgorithmName); + } + } +} diff --git a/crypto/src/crypto/signers/PssSigner.cs b/crypto/src/crypto/signers/PssSigner.cs
index 6900224f3..03890902b 100644 --- a/crypto/src/crypto/signers/PssSigner.cs +++ b/crypto/src/crypto/signers/PssSigner.cs
@@ -115,7 +115,7 @@ namespace Org.BouncyCastle.Crypto.Signers this.trailer = trailer; } - public string AlgorithmName + public virtual string AlgorithmName { get { return mgfDigest.AlgorithmName + "withRSAandMGF1"; } } diff --git a/crypto/src/crypto/signers/RandomDsaKCalculator.cs b/crypto/src/crypto/signers/RandomDsaKCalculator.cs new file mode 100644
index 000000000..022cc268d --- /dev/null +++ b/crypto/src/crypto/signers/RandomDsaKCalculator.cs
@@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class RandomDsaKCalculator + : IDsaKCalculator + { + private BigInteger q; + private SecureRandom random; + + public virtual bool IsDeterministic + { + get { return false; } + } + + public virtual void Init(BigInteger n, SecureRandom random) + { + this.q = n; + this.random = random; + } + + public virtual void Init(BigInteger n, BigInteger d, byte[] message) + { + throw new InvalidOperationException("Operation not supported"); + } + + public virtual BigInteger NextK() + { + int qBitLength = q.BitLength; + + BigInteger k; + do + { + k = new BigInteger(qBitLength, random); + } + while (k.SignValue < 1 || k.CompareTo(q) >= 0); + + return k; + } + } +} diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs
index f57bfc83d..d9b19cf6b 100644 --- a/crypto/src/crypto/signers/RsaDigestSigner.cs +++ b/crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -19,16 +19,16 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Signers { public class RsaDigestSigner - : ISigner + : ISigner { private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine()); private readonly AlgorithmIdentifier algId; - private readonly IDigest digest; - private bool forSigning; + private readonly IDigest digest; + private bool forSigning; - private static readonly IDictionary oidMap = Platform.CreateHashtable(); + private static readonly IDictionary oidMap = Platform.CreateHashtable(); - /// <summary> + /// <summary> /// Load oid table. /// </summary> static RsaDigestSigner() @@ -48,37 +48,36 @@ namespace Org.BouncyCastle.Crypto.Signers oidMap["MD5"] = PkcsObjectIdentifiers.MD5; } - public RsaDigestSigner( - IDigest digest) + public RsaDigestSigner(IDigest digest) + : this(digest, (DerObjectIdentifier)oidMap[digest.AlgorithmName]) { - this.digest = digest; + } - string algName = digest.AlgorithmName; - if (algName.Equals("NULL")) - { - this.algId = null; - } - else - { - this.algId = new AlgorithmIdentifier( - (DerObjectIdentifier)oidMap[digest.AlgorithmName], DerNull.Instance); - } + public RsaDigestSigner(IDigest digest, DerObjectIdentifier digestOid) + : this(digest, new AlgorithmIdentifier(digestOid, DerNull.Instance)) + { } - public string AlgorithmName + public RsaDigestSigner(IDigest digest, AlgorithmIdentifier algId) + { + this.digest = digest; + this.algId = algId; + } + + public virtual 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) + public virtual void Init( + bool forSigning, + ICipherParameters parameters) { this.forSigning = forSigning; AsymmetricKeyParameter k; @@ -95,10 +94,10 @@ namespace Org.BouncyCastle.Crypto.Signers if (forSigning && !k.IsPrivate) throw new InvalidKeyException("Signing requires private key."); - if (!forSigning && k.IsPrivate) + if (!forSigning && k.IsPrivate) throw new InvalidKeyException("Verification requires public key."); - Reset(); + Reset(); rsaEngine.Init(forSigning, parameters); } @@ -106,8 +105,8 @@ namespace Org.BouncyCastle.Crypto.Signers /** * update the internal digest with the byte b */ - public void Update( - byte input) + public virtual void Update( + byte input) { digest.Update(input); } @@ -115,10 +114,10 @@ namespace Org.BouncyCastle.Crypto.Signers /** * update the internal digest with the byte array in */ - public void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate( + byte[] input, + int inOff, + int length) { digest.BlockUpdate(input, inOff, length); } @@ -127,102 +126,92 @@ namespace Org.BouncyCastle.Crypto.Signers * Generate a signature for the message we've been loaded with using * the key we were initialised with. */ - public byte[] GenerateSignature() + public virtual byte[] GenerateSignature() { if (!forSigning) throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation."); - byte[] hash = new byte[digest.GetDigestSize()]; + byte[] hash = new byte[digest.GetDigestSize()]; digest.DoFinal(hash, 0); - byte[] data = DerEncode(hash); + 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) + public virtual 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; + 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) + { + return Arrays.ConstantTimeAreEqual(sig, expected); + } + 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; + + int nonEqual = 0; + + for (int i = 0; i < hash.Length; i++) + { + nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]); + } + + for (int i = 0; i < sigOffset; i++) + { + nonEqual |= (sig[i] ^ expected[i]); // check header less NULL + } + + return nonEqual == 0; + } + else + { + return false; + } } - public void Reset() + public virtual void Reset() { digest.Reset(); } - private byte[] DerEncode(byte[] hash) - { - if (algId == null) - { - // For raw RSA, the DigestInfo must be prepared externally - return hash; - } + private byte[] DerEncode(byte[] hash) + { + if (algId == null) + { + // For raw RSA, the DigestInfo must be prepared externally + return hash; + } - DigestInfo dInfo = new DigestInfo(algId, hash); + DigestInfo dInfo = new DigestInfo(algId, hash); - return dInfo.GetDerEncoded(); - } + return dInfo.GetDerEncoded(); + } } } diff --git a/crypto/src/crypto/signers/X931Signer.cs b/crypto/src/crypto/signers/X931Signer.cs new file mode 100644
index 000000000..36483fe17 --- /dev/null +++ b/crypto/src/crypto/signers/X931Signer.cs
@@ -0,0 +1,225 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * X9.31-1998 - signing using a hash. + * <p> + * The message digest hash, H, is encapsulated to form a byte string as follows + * </p> + * <pre> + * EB = 06 || PS || 0xBA || H || TRAILER + * </pre> + * where PS is a string of bytes all of value 0xBB of length such that |EB|=|n|, and TRAILER is the ISO/IEC 10118 part number† for the digest. The byte string, EB, is converted to an integer value, the message representative, f. + */ + public class X931Signer + : ISigner + { + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_IMPLICIT = 0xBC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_RIPEMD160 = 0x31CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_RIPEMD128 = 0x32CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_SHA1 = 0x33CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_SHA256 = 0x34CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_SHA512 = 0x35CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_SHA384 = 0x36CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_WHIRLPOOL = 0x37CC; + [Obsolete("Use 'IsoTrailers' instead")] + public const int TRAILER_SHA224 = 0x38CC; + + private IDigest digest; + private IAsymmetricBlockCipher cipher; + private RsaKeyParameters kParam; + + private int trailer; + private int keyBits; + private byte[] block; + + /** + * Generate a signer with either implicit or explicit trailers for X9.31. + * + * @param cipher base cipher to use for signature creation/verification + * @param digest digest to use. + * @param implicit whether or not the trailer is implicit or gives the hash. + */ + public X931Signer(IAsymmetricBlockCipher cipher, IDigest digest, bool isImplicit) + { + this.cipher = cipher; + this.digest = digest; + + if (isImplicit) + { + trailer = IsoTrailers.TRAILER_IMPLICIT; + } + else if (IsoTrailers.NoTrailerAvailable(digest)) + { + throw new ArgumentException("no valid trailer", "digest"); + } + else + { + trailer = IsoTrailers.GetTrailer(digest); + } + } + + public virtual string AlgorithmName + { + get { return digest.AlgorithmName + "with" + cipher.AlgorithmName + "/X9.31"; } + } + + /** + * Constructor for a signer with an explicit digest trailer. + * + * @param cipher cipher to use. + * @param digest digest to sign with. + */ + public X931Signer(IAsymmetricBlockCipher cipher, IDigest digest) + : this(cipher, digest, false) + { + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + kParam = (RsaKeyParameters)parameters; + + cipher.Init(forSigning, kParam); + + keyBits = kParam.Modulus.BitLength; + + block = new byte[(keyBits + 7) / 8]; + + Reset(); + } + + /// <summary> clear possible sensitive data</summary> + private void ClearBlock(byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + /** + * update the internal digest with the byte b + */ + public virtual void Update(byte b) + { + digest.Update(b); + } + + /** + * update the internal digest with the byte array in + */ + public virtual void BlockUpdate(byte[] input, int off, int len) + { + digest.BlockUpdate(input, off, len); + } + + /** + * reset the internal state + */ + public virtual void Reset() + { + digest.Reset(); + } + + /** + * generate a signature for the loaded message using the key we were + * initialised with. + */ + public virtual byte[] GenerateSignature() + { + CreateSignatureBlock(); + + BigInteger t = new BigInteger(1, cipher.ProcessBlock(block, 0, block.Length)); + ClearBlock(block); + + t = t.Min(kParam.Modulus.Subtract(t)); + + return BigIntegers.AsUnsignedByteArray((kParam.Modulus.BitLength + 7) / 8, t); + } + + private void CreateSignatureBlock() + { + int digSize = digest.GetDigestSize(); + + int delta; + if (trailer == IsoTrailers.TRAILER_IMPLICIT) + { + delta = block.Length - digSize - 1; + digest.DoFinal(block, delta); + block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT; + } + else + { + delta = block.Length - digSize - 2; + digest.DoFinal(block, delta); + block[block.Length - 2] = (byte)(trailer >> 8); + block[block.Length - 1] = (byte)trailer; + } + + block[0] = 0x6b; + for (int i = delta - 2; i != 0; i--) + { + block[i] = (byte)0xbb; + } + block[delta - 1] = (byte)0xba; + } + + /** + * return true if the signature represents a ISO9796-2 signature + * for the passed in message. + */ + public virtual bool VerifySignature(byte[] signature) + { + try + { + block = cipher.ProcessBlock(signature, 0, signature.Length); + } + catch (Exception) + { + return false; + } + + BigInteger t = new BigInteger(block); + BigInteger f; + + if ((t.IntValue & 15) == 12) + { + f = t; + } + else + { + t = kParam.Modulus.Subtract(t); + if ((t.IntValue & 15) == 12) + { + f = t; + } + else + { + return false; + } + } + + CreateSignatureBlock(); + + byte[] fBlock = BigIntegers.AsUnsignedByteArray(block.Length, f); + + bool rv = Arrays.ConstantTimeAreEqual(block, fBlock); + + ClearBlock(block); + ClearBlock(fBlock); + + return rv; + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs new file mode 100644
index 000000000..2d7af80e8 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsAgreementCredentials + : AbstractTlsCredentials, TlsAgreementCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs new file mode 100644
index 000000000..141ee6507 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs
@@ -0,0 +1,15 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class AbstractTlsCipherFactory + : TlsCipherFactory + { + /// <exception cref="IOException"></exception> + public virtual TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs new file mode 100644
index 000000000..046feb78c --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -0,0 +1,258 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsClient + : AbstractTlsPeer, TlsClient + { + protected TlsCipherFactory mCipherFactory; + + protected TlsClientContext mContext; + + protected IList mSupportedSignatureAlgorithms; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; + + protected int mSelectedCipherSuite; + protected short mSelectedCompressionMethod; + + public AbstractTlsClient() + : this(new DefaultTlsCipherFactory()) + { + } + + public AbstractTlsClient(TlsCipherFactory cipherFactory) + { + this.mCipherFactory = cipherFactory; + } + + protected virtual bool AllowUnexpectedServerExtension(int extensionType, byte[] extensionData) + { + switch (extensionType) + { + case ExtensionType.elliptic_curves: + /* + * Exception added based on field reports that some servers do send this, although the + * Supported Elliptic Curves Extension is clearly intended to be client-only. If + * present, we still require that it is a valid EllipticCurveList. + */ + TlsEccUtilities.ReadSupportedEllipticCurvesExtension(extensionData); + return true; + default: + return false; + } + } + + protected virtual void CheckForUnexpectedServerExtension(IDictionary serverExtensions, int extensionType) + { + byte[] extensionData = TlsUtilities.GetExtensionData(serverExtensions, extensionType); + if (extensionData != null && !AllowUnexpectedServerExtension(extensionType, extensionData)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public virtual void Init(TlsClientContext context) + { + this.mContext = context; + } + + public virtual TlsSession GetSessionToResume() + { + return null; + } + + /** + * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value + * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest + * version number supported by the client, and the value of ClientHello.client_version. No + * single value will guarantee interoperability with all old servers, but this is a complex + * topic beyond the scope of this document." + */ + public virtual ProtocolVersion ClientHelloRecordLayerVersion + { + get + { + // "{03,00}" + // return ProtocolVersion.SSLv3; + + // "the lowest version number supported by the client" + // return getMinimumVersion(); + + // "the value of ClientHello.client_version" + return ClientVersion; + } + } + + public virtual ProtocolVersion ClientVersion + { + get { return ProtocolVersion.TLSv12; } + } + + public virtual bool IsFallback + { + /* + * draft-ietf-tls-downgrade-scsv-00 4. [..] is meant for use by clients that repeat a + * connection attempt with a downgraded protocol in order to avoid interoperability problems + * with legacy servers. + */ + get { return false; } + } + + public virtual IDictionary GetClientExtensions() + { + IDictionary clientExtensions = null; + + ProtocolVersion clientVersion = mContext.ClientVersion; + + /* + * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2. + * Clients MUST NOT offer it if they are offering prior versions. + */ + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + // TODO Provide a way for the user to specify the acceptable hash/signature algorithms. + + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(); + + clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions); + + TlsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, mSupportedSignatureAlgorithms); + } + + if (TlsEccUtilities.ContainsEccCipherSuites(GetCipherSuites())) + { + /* + * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message + * appends these extensions (along with any others), enumerating the curves it supports + * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic + * Curves Extension and the Supported Point Formats Extension. + */ + /* + * TODO Could just add all the curves since we support them all, but users may not want + * to use unnecessarily large fields. Need configuration options. + */ + this.mNamedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 }; + this.mClientECPointFormats = new byte[]{ ECPointFormat.uncompressed, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; + + clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions); + + TlsEccUtilities.AddSupportedEllipticCurvesExtension(clientExtensions, mNamedCurves); + TlsEccUtilities.AddSupportedPointFormatsExtension(clientExtensions, mClientECPointFormats); + } + + return clientExtensions; + } + + public virtual ProtocolVersion MinimumVersion + { + get { return ProtocolVersion.TLSv10; } + } + + public virtual void NotifyServerVersion(ProtocolVersion serverVersion) + { + if (!MinimumVersion.IsEqualOrEarlierVersionOf(serverVersion)) + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + + public abstract int[] GetCipherSuites(); + + public virtual byte[] GetCompressionMethods() + { + return new byte[]{ CompressionMethod.cls_null }; + } + + public virtual void NotifySessionID(byte[] sessionID) + { + // Currently ignored + } + + public virtual void NotifySelectedCipherSuite(int selectedCipherSuite) + { + this.mSelectedCipherSuite = selectedCipherSuite; + } + + public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod) + { + this.mSelectedCompressionMethod = selectedCompressionMethod; + } + + public virtual void ProcessServerExtensions(IDictionary serverExtensions) + { + /* + * TlsProtocol implementation validates that any server extensions received correspond to + * client extensions sent. By default, we don't send any, and this method is not called. + */ + if (serverExtensions != null) + { + /* + * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension. + */ + CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.signature_algorithms); + + CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.elliptic_curves); + + if (TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite)) + { + this.mServerECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(serverExtensions); + } + else + { + CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.ec_point_formats); + } + } + } + + public virtual void ProcessServerSupplementalData(IList serverSupplementalData) + { + if (serverSupplementalData != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public abstract TlsKeyExchange GetKeyExchange(); + + public abstract TlsAuthentication GetAuthentication(); + + public virtual IList GetClientSupplementalData() + { + return null; + } + + public override TlsCompression GetCompression() + { + switch (mSelectedCompressionMethod) + { + case CompressionMethod.cls_null: + return new TlsNullCompression(); + + case CompressionMethod.DEFLATE: + return new TlsDeflateCompression(); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected compression method was in the list of client-offered compression + * methods, so if we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsCipher GetCipher() + { + int encryptionAlgorithm = TlsUtilities.GetEncryptionAlgorithm(mSelectedCipherSuite); + int macAlgorithm = TlsUtilities.GetMacAlgorithm(mSelectedCipherSuite); + + return mCipherFactory.CreateCipher(mContext, encryptionAlgorithm, macAlgorithm); + } + + public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket) + { + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs new file mode 100644
index 000000000..ae7efc64d --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -0,0 +1,152 @@ +using System; +using System.Threading; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal abstract class AbstractTlsContext + : TlsContext + { + private static long counter = Times.NanoTime(); + +#if NETCF_1_0 + private static object counterLock = new object(); + private static long NextCounterValue() + { + lock (counterLock) + { + return ++counter; + } + } +#else + private static long NextCounterValue() + { + return Interlocked.Increment(ref counter); + } +#endif + + private readonly IRandomGenerator mNonceRandom; + private readonly SecureRandom mSecureRandom; + private readonly SecurityParameters mSecurityParameters; + + private ProtocolVersion mClientVersion = null; + private ProtocolVersion mServerVersion = null; + private TlsSession mSession = null; + private object mUserObject = null; + + internal AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters) + { + IDigest d = TlsUtilities.CreateHash(HashAlgorithm.sha256); + byte[] seed = new byte[d.GetDigestSize()]; + secureRandom.NextBytes(seed); + + this.mNonceRandom = new DigestRandomGenerator(d); + mNonceRandom.AddSeedMaterial(NextCounterValue()); + mNonceRandom.AddSeedMaterial(Times.NanoTime()); + mNonceRandom.AddSeedMaterial(seed); + + this.mSecureRandom = secureRandom; + this.mSecurityParameters = securityParameters; + } + + public virtual IRandomGenerator NonceRandomGenerator + { + get { return mNonceRandom; } + } + + public virtual SecureRandom SecureRandom + { + get { return mSecureRandom; } + } + + public virtual SecurityParameters SecurityParameters + { + get { return mSecurityParameters; } + } + + public abstract bool IsServer { get; } + + public virtual ProtocolVersion ClientVersion + { + get { return mClientVersion; } + } + + internal virtual void SetClientVersion(ProtocolVersion clientVersion) + { + this.mClientVersion = clientVersion; + } + + public virtual ProtocolVersion ServerVersion + { + get { return mServerVersion; } + } + + internal virtual void SetServerVersion(ProtocolVersion serverVersion) + { + this.mServerVersion = serverVersion; + } + + public virtual TlsSession ResumableSession + { + get { return mSession; } + } + + internal virtual void SetResumableSession(TlsSession session) + { + this.mSession = session; + } + + public virtual object UserObject + { + get { return mUserObject; } + set { this.mUserObject = value; } + } + + public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length) + { + /* + * TODO[session-hash] + * + * draft-ietf-tls-session-hash-04 5.4. If a client or server chooses to continue with a full + * handshake without the extended master secret extension, [..] the client or server MUST + * NOT export any key material based on the new master secret for any subsequent + * application-level authentication. In particular, it MUST disable [RFC5705] [..]. + */ + + if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length)) + throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value"); + + SecurityParameters sp = SecurityParameters; + byte[] cr = sp.ClientRandom, sr = sp.ServerRandom; + + int seedLength = cr.Length + sr.Length; + if (context_value != null) + { + seedLength += (2 + context_value.Length); + } + + byte[] seed = new byte[seedLength]; + int seedPos = 0; + + Array.Copy(cr, 0, seed, seedPos, cr.Length); + seedPos += cr.Length; + Array.Copy(sr, 0, seed, seedPos, sr.Length); + seedPos += sr.Length; + if (context_value != null) + { + TlsUtilities.WriteUint16(context_value.Length, seed, seedPos); + seedPos += 2; + Array.Copy(context_value, 0, seed, seedPos, context_value.Length); + seedPos += context_value.Length; + } + + if (seedPos != seedLength) + throw new InvalidOperationException("error in calculation of seed for export"); + + return TlsUtilities.PRF(this, sp.MasterSecret, asciiLabel, seed, length); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsCredentials.cs b/crypto/src/crypto/tls/AbstractTlsCredentials.cs new file mode 100644
index 000000000..6411b811c --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsCredentials.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsCredentials + : TlsCredentials + { + public abstract Certificate Certificate { get; } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs new file mode 100644
index 000000000..05b129c60 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsEncryptionCredentials + : AbstractTlsCredentials, TlsEncryptionCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs new file mode 100644
index 000000000..c9ec06107 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
@@ -0,0 +1,166 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsKeyExchange + : TlsKeyExchange + { + protected readonly int mKeyExchange; + protected IList mSupportedSignatureAlgorithms; + + protected TlsContext mContext; + + protected AbstractTlsKeyExchange(int keyExchange, IList supportedSignatureAlgorithms) + { + this.mKeyExchange = keyExchange; + this.mSupportedSignatureAlgorithms = supportedSignatureAlgorithms; + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + + ProtocolVersion clientVersion = context.ClientVersion; + + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + /* + * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension, + * the server MUST do the following: + * + * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK, + * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}. + * + * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if + * the client had sent the value {sha1,dsa}. + * + * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA), + * behave as if the client had sent value {sha1,ecdsa}. + */ + if (this.mSupportedSignatureAlgorithms == null) + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.DH_DSS: + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.SRP_DSS: + { + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultDssSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.ECDH_ECDSA: + case KeyExchangeAlgorithm.ECDHE_ECDSA: + { + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.DH_RSA: + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.ECDH_RSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + case KeyExchangeAlgorithm.RSA: + case KeyExchangeAlgorithm.RSA_PSK: + case KeyExchangeAlgorithm.SRP_RSA: + { + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultRsaSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.SRP: + break; + + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); + } + } + + } + else if (this.mSupportedSignatureAlgorithms != null) + { + throw new InvalidOperationException("supported_signature_algorithms not allowed for " + clientVersion); + } + } + + public abstract void SkipServerCredentials(); + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + if (mSupportedSignatureAlgorithms == null) + { + /* + * TODO RFC 2264 7.4.2. Unless otherwise specified, the signing algorithm for the + * certificate must be the same as the algorithm for the certificate key. + */ + } + else + { + /* + * TODO RFC 5264 7.4.2. If the client provided a "signature_algorithms" extension, then + * all certificates provided by the server MUST be signed by a hash/signature algorithm + * pair that appears in that extension. + */ + } + } + + public virtual void ProcessServerCredentials(TlsCredentials serverCredentials) + { + ProcessServerCertificate(serverCredentials.Certificate); + } + + public virtual bool RequiresServerKeyExchange + { + get { return false; } + } + + public virtual byte[] GenerateServerKeyExchange() + { + if (RequiresServerKeyExchange) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return null; + } + + public virtual void SkipServerKeyExchange() + { + if (RequiresServerKeyExchange) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + if (!RequiresServerKeyExchange) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public abstract void ValidateCertificateRequest(CertificateRequest certificateRequest); + + public virtual void SkipClientCredentials() + { + } + + public abstract void ProcessClientCredentials(TlsCredentials clientCredentials); + + public virtual void ProcessClientCertificate(Certificate clientCertificate) + { + } + + public abstract void GenerateClientKeyExchange(Stream output); + + public virtual void ProcessClientKeyExchange(Stream input) + { + // Key exchange implementation MUST support client key exchange + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public abstract byte[] GeneratePremasterSecret(); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs new file mode 100644
index 000000000..81a53386c --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs
@@ -0,0 +1,48 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsPeer + : TlsPeer + { + public virtual bool ShouldUseGmtUnixTime() + { + /* + * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that + * TLS implementors MUST by default set the entire value the ClientHello.Random and + * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random + * sequence. + */ + return false; + } + + public virtual void NotifySecureRenegotiation(bool secureRenegotiation) + { + if (!secureRenegotiation) + { + /* + * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead + * of continuing; see Section 4.1/4.3 for discussion. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + public abstract TlsCompression GetCompression(); + + public abstract TlsCipher GetCipher(); + + public virtual void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause) + { + } + + public virtual void NotifyAlertReceived(byte alertLevel, byte alertDescription) + { + } + + public virtual void NotifyHandshakeComplete() + { + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs new file mode 100644
index 000000000..9f549cfd1 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -0,0 +1,349 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsServer + : AbstractTlsPeer, TlsServer + { + protected TlsCipherFactory mCipherFactory; + + protected TlsServerContext mContext; + + protected ProtocolVersion mClientVersion; + protected int[] mOfferedCipherSuites; + protected byte[] mOfferedCompressionMethods; + protected IDictionary mClientExtensions; + + protected bool mEncryptThenMacOffered; + protected short mMaxFragmentLengthOffered; + protected bool mTruncatedHMacOffered; + protected IList mSupportedSignatureAlgorithms; + protected bool mEccCipherSuitesOffered; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; + + protected ProtocolVersion mServerVersion; + protected int mSelectedCipherSuite; + protected byte mSelectedCompressionMethod; + protected IDictionary mServerExtensions; + + public AbstractTlsServer() + : this(new DefaultTlsCipherFactory()) + { + } + + public AbstractTlsServer(TlsCipherFactory cipherFactory) + { + this.mCipherFactory = cipherFactory; + } + + protected virtual bool AllowEncryptThenMac + { + get { return true; } + } + + protected virtual bool AllowTruncatedHMac + { + get { return false; } + } + + protected virtual IDictionary CheckServerExtensions() + { + return this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mServerExtensions); + } + + protected abstract int[] GetCipherSuites(); + + protected byte[] GetCompressionMethods() + { + return new byte[] { CompressionMethod.cls_null }; + } + + protected virtual ProtocolVersion MaximumVersion + { + get { return ProtocolVersion.TLSv11; } + } + + protected virtual ProtocolVersion MinimumVersion + { + get { return ProtocolVersion.TLSv10; } + } + + protected virtual bool SupportsClientEccCapabilities(int[] namedCurves, byte[] ecPointFormats) + { + // NOTE: BC supports all the current set of point formats so we don't check them here + + if (namedCurves == null) + { + /* + * RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these + * extensions. In this case, the server is free to choose any one of the elliptic curves + * or point formats [...]. + */ + return TlsEccUtilities.HasAnySupportedNamedCurves(); + } + + for (int i = 0; i < namedCurves.Length; ++i) + { + int namedCurve = namedCurves[i]; + if (NamedCurve.IsValid(namedCurve) + && (!NamedCurve.RefersToASpecificNamedCurve(namedCurve) || TlsEccUtilities.IsSupportedNamedCurve(namedCurve))) + { + return true; + } + } + + return false; + } + + public virtual void Init(TlsServerContext context) + { + this.mContext = context; + } + + public virtual void NotifyClientVersion(ProtocolVersion clientVersion) + { + this.mClientVersion = clientVersion; + } + + public virtual void NotifyFallback(bool isFallback) + { + /* + * draft-ietf-tls-downgrade-scsv-00 3. If TLS_FALLBACK_SCSV appears in + * ClientHello.cipher_suites and the highest protocol version supported by the server is + * higher than the version indicated in ClientHello.client_version, the server MUST respond + * with an inappropriate_fallback alert. + */ + if (isFallback && MaximumVersion.IsLaterVersionOf(mClientVersion)) + throw new TlsFatalAlert(AlertDescription.inappropriate_fallback); + } + + public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites) + { + this.mOfferedCipherSuites = offeredCipherSuites; + this.mEccCipherSuitesOffered = TlsEccUtilities.ContainsEccCipherSuites(this.mOfferedCipherSuites); + } + + public virtual void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods) + { + this.mOfferedCompressionMethods = offeredCompressionMethods; + } + + public virtual void ProcessClientExtensions(IDictionary clientExtensions) + { + this.mClientExtensions = clientExtensions; + + if (clientExtensions != null) + { + this.mEncryptThenMacOffered = TlsExtensionsUtilities.HasEncryptThenMacExtension(clientExtensions); + + this.mMaxFragmentLengthOffered = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions); + if (mMaxFragmentLengthOffered >= 0 && !MaxFragmentLength.IsValid((byte)mMaxFragmentLengthOffered)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mTruncatedHMacOffered = TlsExtensionsUtilities.HasTruncatedHMacExtension(clientExtensions); + + this.mSupportedSignatureAlgorithms = TlsUtilities.GetSignatureAlgorithmsExtension(clientExtensions); + if (this.mSupportedSignatureAlgorithms != null) + { + /* + * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior + * to 1.2. Clients MUST NOT offer it if they are offering prior versions. + */ + if (!TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mClientVersion)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.mNamedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(clientExtensions); + this.mClientECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(clientExtensions); + } + + /* + * RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it + * does not propose any ECC cipher suites. + * + * NOTE: This was overly strict as there may be ECC cipher suites that we don't recognize. + * Also, draft-ietf-tls-negotiated-ff-dhe will be overloading the 'elliptic_curves' + * extension to explicitly allow FFDHE (i.e. non-ECC) groups. + */ + //if (!this.mEccCipherSuitesOffered && (this.mNamedCurves != null || this.mClientECPointFormats != null)) + // throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public virtual ProtocolVersion GetServerVersion() + { + if (MinimumVersion.IsEqualOrEarlierVersionOf(mClientVersion)) + { + ProtocolVersion maximumVersion = MaximumVersion; + if (mClientVersion.IsEqualOrEarlierVersionOf(maximumVersion)) + { + return mServerVersion = mClientVersion; + } + if (mClientVersion.IsLaterVersionOf(maximumVersion)) + { + return mServerVersion = maximumVersion; + } + } + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + + public virtual int GetSelectedCipherSuite() + { + /* + * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate + * cipher suites against the "signature_algorithms" extension before selecting them. This is + * somewhat inelegant but is a compromise designed to minimize changes to the original + * cipher suite design. + */ + + /* + * RFC 4429 5.1. A server that receives a ClientHello containing one or both of these + * extensions MUST use the client's enumerated capabilities to guide its selection of an + * appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only + * if the server can successfully complete the handshake while using the curves and point + * formats supported by the client [...]. + */ + bool eccCipherSuitesEnabled = SupportsClientEccCapabilities(this.mNamedCurves, this.mClientECPointFormats); + + int[] cipherSuites = GetCipherSuites(); + for (int i = 0; i < cipherSuites.Length; ++i) + { + int cipherSuite = cipherSuites[i]; + + if (Arrays.Contains(this.mOfferedCipherSuites, cipherSuite) + && (eccCipherSuitesEnabled || !TlsEccUtilities.IsEccCipherSuite(cipherSuite)) + && TlsUtilities.IsValidCipherSuiteForVersion(cipherSuite, mServerVersion)) + { + return this.mSelectedCipherSuite = cipherSuite; + } + } + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + public virtual byte GetSelectedCompressionMethod() + { + byte[] compressionMethods = GetCompressionMethods(); + for (int i = 0; i < compressionMethods.Length; ++i) + { + if (Arrays.Contains(mOfferedCompressionMethods, compressionMethods[i])) + { + return this.mSelectedCompressionMethod = compressionMethods[i]; + } + } + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + // IDictionary is (Int32 -> byte[]) + public virtual IDictionary GetServerExtensions() + { + if (this.mEncryptThenMacOffered && AllowEncryptThenMac) + { + /* + * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client + * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) + * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the + * client. + */ + if (TlsUtilities.IsBlockCipherSuite(this.mSelectedCipherSuite)) + { + TlsExtensionsUtilities.AddEncryptThenMacExtension(CheckServerExtensions()); + } + } + + if (this.mMaxFragmentLengthOffered >= 0 + && TlsUtilities.IsValidUint8(mMaxFragmentLengthOffered) + && MaxFragmentLength.IsValid((byte)mMaxFragmentLengthOffered)) + { + TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)mMaxFragmentLengthOffered); + } + + if (this.mTruncatedHMacOffered && AllowTruncatedHMac) + { + TlsExtensionsUtilities.AddTruncatedHMacExtension(CheckServerExtensions()); + } + + if (this.mClientECPointFormats != null && TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite)) + { + /* + * RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello + * message including a Supported Point Formats Extension appends this extension (along + * with others) to its ServerHello message, enumerating the point formats it can parse. + */ + this.mServerECPointFormats = new byte[]{ ECPointFormat.uncompressed, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; + + TlsEccUtilities.AddSupportedPointFormatsExtension(CheckServerExtensions(), mServerECPointFormats); + } + + return mServerExtensions; + } + + public virtual IList GetServerSupplementalData() + { + return null; + } + + public abstract TlsCredentials GetCredentials(); + + public virtual CertificateStatus GetCertificateStatus() + { + return null; + } + + public abstract TlsKeyExchange GetKeyExchange(); + + public virtual CertificateRequest GetCertificateRequest() + { + return null; + } + + public virtual void ProcessClientSupplementalData(IList clientSupplementalData) + { + if (clientSupplementalData != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void NotifyClientCertificate(Certificate clientCertificate) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public override TlsCompression GetCompression() + { + switch (mSelectedCompressionMethod) + { + case CompressionMethod.cls_null: + return new TlsNullCompression(); + + default: + /* + * Note: internal error here; we selected the compression method, so if we now can't + * produce an implementation, we shouldn't have chosen it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsCipher GetCipher() + { + int encryptionAlgorithm = TlsUtilities.GetEncryptionAlgorithm(mSelectedCipherSuite); + int macAlgorithm = TlsUtilities.GetMacAlgorithm(mSelectedCipherSuite); + + return mCipherFactory.CreateCipher(mContext, encryptionAlgorithm, macAlgorithm); + } + + public virtual NewSessionTicket GetNewSessionTicket() + { + /* + * RFC 5077 3.3. If the server determines that it does not want to include a ticket after it + * has included the SessionTicket extension in the ServerHello, then it sends a zero-length + * ticket in the NewSessionTicket handshake message. + */ + return new NewSessionTicket(0L, TlsUtilities.EmptyBytes); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsSigner.cs b/crypto/src/crypto/tls/AbstractTlsSigner.cs new file mode 100644
index 000000000..1f4aabf74 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsSigner.cs
@@ -0,0 +1,50 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsSigner + : TlsSigner + { + protected TlsContext mContext; + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + { + return GenerateRawSignature(null, privateKey, md5AndSha1); + } + + public abstract byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash); + + public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + { + return VerifyRawSignature(null, sigBytes, publicKey, md5AndSha1); + } + + public abstract bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash); + + public virtual ISigner CreateSigner(AsymmetricKeyParameter privateKey) + { + return CreateSigner(null, privateKey); + } + + public abstract ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); + + public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + { + return CreateVerifyer(null, publicKey); + } + + public abstract ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); + + public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs new file mode 100644
index 000000000..886c46c6e --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs
@@ -0,0 +1,20 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsSignerCredentials + : AbstractTlsCredentials, TlsSignerCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] GenerateCertificateSignature(byte[] hash); + + public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get + { + throw new InvalidOperationException("TlsSignerCredentials implementation does not support (D)TLS 1.2+"); + } + } + } +} diff --git a/crypto/src/crypto/tls/AlertDescription.cs b/crypto/src/crypto/tls/AlertDescription.cs
index e09da6cab..49de60cea 100644 --- a/crypto/src/crypto/tls/AlertDescription.cs +++ b/crypto/src/crypto/tls/AlertDescription.cs
@@ -213,5 +213,92 @@ namespace Org.BouncyCastle.Crypto.Tls * "unknown_psk_identity" alert message. */ public const byte unknown_psk_identity = 115; + + /* + * draft-ietf-tls-downgrade-scsv-00 + */ + + /** + * If TLS_FALLBACK_SCSV appears in ClientHello.cipher_suites and the highest protocol version + * supported by the server is higher than the version indicated in ClientHello.client_version, + * the server MUST respond with an inappropriate_fallback alert. + */ + public const byte inappropriate_fallback = 86; + + public static string GetName(byte alertDescription) + { + switch (alertDescription) + { + case close_notify: + return "close_notify"; + case unexpected_message: + return "unexpected_message"; + case bad_record_mac: + return "bad_record_mac"; + case decryption_failed: + return "decryption_failed"; + case record_overflow: + return "record_overflow"; + case decompression_failure: + return "decompression_failure"; + case handshake_failure: + return "handshake_failure"; + case no_certificate: + return "no_certificate"; + case bad_certificate: + return "bad_certificate"; + case unsupported_certificate: + return "unsupported_certificate"; + case certificate_revoked: + return "certificate_revoked"; + case certificate_expired: + return "certificate_expired"; + case certificate_unknown: + return "certificate_unknown"; + case illegal_parameter: + return "illegal_parameter"; + case unknown_ca: + return "unknown_ca"; + case access_denied: + return "access_denied"; + case decode_error: + return "decode_error"; + case decrypt_error: + return "decrypt_error"; + case export_restriction: + return "export_restriction"; + case protocol_version: + return "protocol_version"; + case insufficient_security: + return "insufficient_security"; + case internal_error: + return "internal_error"; + case user_canceled: + return "user_canceled"; + case no_renegotiation: + return "no_renegotiation"; + case unsupported_extension: + return "unsupported_extension"; + case certificate_unobtainable: + return "certificate_unobtainable"; + case unrecognized_name: + return "unrecognized_name"; + case bad_certificate_status_response: + return "bad_certificate_status_response"; + case bad_certificate_hash_value: + return "bad_certificate_hash_value"; + case unknown_psk_identity: + return "unknown_psk_identity"; + case inappropriate_fallback: + return "inappropriate_fallback"; + default: + return "UNKNOWN"; + } + } + + public static string GetText(byte alertDescription) + { + return GetName(alertDescription) + "(" + alertDescription + ")"; + } } } diff --git a/crypto/src/crypto/tls/AlertLevel.cs b/crypto/src/crypto/tls/AlertLevel.cs
index d77251dfb..9461a0b58 100644 --- a/crypto/src/crypto/tls/AlertLevel.cs +++ b/crypto/src/crypto/tls/AlertLevel.cs
@@ -7,5 +7,23 @@ namespace Org.BouncyCastle.Crypto.Tls { public const byte warning = 1; public const byte fatal = 2; + + public static string GetName(byte alertDescription) + { + switch (alertDescription) + { + case warning: + return "warning"; + case fatal: + return "fatal"; + default: + return "UNKNOWN"; + } + } + + public static string GetText(byte alertDescription) + { + return GetName(alertDescription) + "(" + alertDescription + ")"; + } } } diff --git a/crypto/src/crypto/tls/AlwaysValidVerifyer.cs b/crypto/src/crypto/tls/AlwaysValidVerifyer.cs deleted file mode 100644
index e26c6fc3f..000000000 --- a/crypto/src/crypto/tls/AlwaysValidVerifyer.cs +++ /dev/null
@@ -1,24 +0,0 @@ -using System; - -using Org.BouncyCastle.Asn1.X509; - -namespace Org.BouncyCastle.Crypto.Tls -{ - /// <remarks> - /// A certificate verifyer, that will always return true. - /// <pre> - /// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING. - /// </pre> - /// </remarks> - [Obsolete("Perform certificate verification in TlsAuthentication implementation")] - public class AlwaysValidVerifyer - : ICertificateVerifyer - { - /// <summary>Return true.</summary> - public bool IsValid( - X509CertificateStructure[] certs) - { - return true; - } - } -} diff --git a/crypto/src/crypto/tls/BasicTlsPskIdentity.cs b/crypto/src/crypto/tls/BasicTlsPskIdentity.cs new file mode 100644
index 000000000..db5954422 --- /dev/null +++ b/crypto/src/crypto/tls/BasicTlsPskIdentity.cs
@@ -0,0 +1,43 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class BasicTlsPskIdentity + : TlsPskIdentity + { + protected byte[] mIdentity; + protected byte[] mPsk; + + public BasicTlsPskIdentity(byte[] identity, byte[] psk) + { + this.mIdentity = Arrays.Clone(identity); + this.mPsk = Arrays.Clone(psk); + } + + public BasicTlsPskIdentity(string identity, byte[] psk) + { + this.mIdentity = Strings.ToUtf8ByteArray(identity); + this.mPsk = Arrays.Clone(psk); + } + + public virtual void SkipIdentityHint() + { + } + + public virtual void NotifyIdentityHint(byte[] psk_identity_hint) + { + } + + public virtual byte[] GetPskIdentity() + { + return mIdentity; + } + + public virtual byte[] GetPsk() + { + return mPsk; + } + } +} diff --git a/crypto/src/crypto/tls/ByteQueueStream.cs b/crypto/src/crypto/tls/ByteQueueStream.cs new file mode 100644
index 000000000..bf603e006 --- /dev/null +++ b/crypto/src/crypto/tls/ByteQueueStream.cs
@@ -0,0 +1,114 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ByteQueueStream + : Stream + { + private readonly ByteQueue buffer; + + public ByteQueueStream() + { + this.buffer = new ByteQueue(); + } + + public virtual int Available + { + get { return buffer.Available; } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override void Close() + { + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public virtual int Peek(byte[] buf) + { + int bytesToRead = System.Math.Min(buffer.Available, buf.Length); + buffer.Read(buf, 0, bytesToRead, 0); + return bytesToRead; + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public virtual int Read(byte[] buf) + { + return Read(buf, 0, buf.Length); + } + + public override int Read(byte[] buf, int off, int len) + { + int bytesToRead = System.Math.Min(buffer.Available, len); + buffer.RemoveData(buf, off, bytesToRead, 0); + return bytesToRead; + } + + public override int ReadByte() + { + if (buffer.Available == 0) + return -1; + + return buffer.RemoveData(1, 0)[0] & 0xFF; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public virtual int Skip(int n) + { + int bytesToSkip = System.Math.Min(buffer.Available, n); + buffer.RemoveData(bytesToSkip); + return bytesToSkip; + } + + public virtual void Write(byte[] buf) + { + buffer.AddData(buf, 0, buf.Length); + } + + public override void Write(byte[] buf, int off, int len) + { + buffer.AddData(buf, off, len); + } + + public override void WriteByte(byte b) + { + buffer.AddData(new byte[]{ b }, 0, 1); + } + } +} diff --git a/crypto/src/crypto/tls/CertChainType.cs b/crypto/src/crypto/tls/CertChainType.cs
index b526a79a7..cbb183441 100644 --- a/crypto/src/crypto/tls/CertChainType.cs +++ b/crypto/src/crypto/tls/CertChainType.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls */ public abstract class CertChainType { - public const short individual_certs = 0; - public const short pkipath = 1; + public const byte individual_certs = 0; + public const byte pkipath = 1; - public static bool IsValid(short certChainType) + public static bool IsValid(byte certChainType) { return certChainType >= individual_certs && certChainType <= pkipath; } diff --git a/crypto/src/crypto/tls/Certificate.cs b/crypto/src/crypto/tls/Certificate.cs
index 12bfa9214..c59616c95 100644 --- a/crypto/src/crypto/tls/Certificate.cs +++ b/crypto/src/crypto/tls/Certificate.cs
@@ -38,13 +38,6 @@ namespace Org.BouncyCastle.Crypto.Tls this.mCertificateList = certificateList; } - /// <returns>An array which contains the certs, this chain contains.</returns> - [Obsolete("Use 'GetCertificateList' instead")] - public virtual X509CertificateStructure[] GetCerts() - { - return GetCertificateList(); - } - /** * @return an array of {@link org.bouncycastle.asn1.x509.Certificate} representing a certificate * chain. diff --git a/crypto/src/crypto/tls/CertificateRequest.cs b/crypto/src/crypto/tls/CertificateRequest.cs
index 8ab265513..f3dcb3bbd 100644 --- a/crypto/src/crypto/tls/CertificateRequest.cs +++ b/crypto/src/crypto/tls/CertificateRequest.cs
@@ -100,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Tls { byte[] derEncoding = certificateAuthority.GetEncoded(Asn1Encodable.Der); derEncodings.Add(derEncoding); - totalLength += derEncoding.Length; + totalLength += derEncoding.Length + 2; } TlsUtilities.CheckUint16(totalLength); @@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Crypto.Tls foreach (byte[] derEncoding in derEncodings) { - output.Write(derEncoding, 0, derEncoding.Length); + TlsUtilities.WriteOpaque16(derEncoding, output); } } } @@ -123,8 +123,7 @@ namespace Org.BouncyCastle.Crypto.Tls * @return a {@link CertificateRequest} object. * @throws IOException */ - public static CertificateRequest Parse(//TlsContext context, - Stream input) + public static CertificateRequest Parse(TlsContext context, Stream input) { int numTypes = TlsUtilities.ReadUint8(input); byte[] certificateTypes = new byte[numTypes]; @@ -133,13 +132,12 @@ namespace Org.BouncyCastle.Crypto.Tls certificateTypes[i] = TlsUtilities.ReadUint8(input); } - // TODO Add TLS 1.2 support here IList supportedSignatureAlgorithms = null; - //if (TlsUtilities.IsTLSv12(context)) - //{ - // // TODO Check whether SignatureAlgorithm.anonymous is allowed here - // supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input); - //} + if (TlsUtilities.IsTlsV12(context)) + { + // TODO Check whether SignatureAlgorithm.anonymous is allowed here + supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input); + } IList certificateAuthorities = Platform.CreateArrayList(); byte[] certAuthData = TlsUtilities.ReadOpaque16(input); diff --git a/crypto/src/crypto/tls/CertificateStatus.cs b/crypto/src/crypto/tls/CertificateStatus.cs new file mode 100644
index 000000000..0f95475b9 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateStatus.cs
@@ -0,0 +1,102 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class CertificateStatus + { + protected readonly byte mStatusType; + protected readonly object mResponse; + + public CertificateStatus(byte statusType, object response) + { + if (!IsCorrectType(statusType, response)) + throw new ArgumentException("not an instance of the correct type", "response"); + + this.mStatusType = statusType; + this.mResponse = response; + } + + public virtual byte StatusType + { + get { return mStatusType; } + } + + public virtual object Response + { + get { return mResponse; } + } + + public virtual OcspResponse GetOcspResponse() + { + if (!IsCorrectType(CertificateStatusType.ocsp, mResponse)) + throw new InvalidOperationException("'response' is not an OcspResponse"); + + return (OcspResponse)mResponse; + } + + /** + * Encode this {@link CertificateStatus} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mStatusType, output); + + switch (mStatusType) + { + case CertificateStatusType.ocsp: + byte[] derEncoding = ((OcspResponse)mResponse).GetEncoded(Asn1Encodable.Der); + TlsUtilities.WriteOpaque24(derEncoding, output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link CertificateStatus} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateStatus} object. + * @throws IOException + */ + public static CertificateStatus Parse(Stream input) + { + byte status_type = TlsUtilities.ReadUint8(input); + object response; + + switch (status_type) + { + case CertificateStatusType.ocsp: + { + byte[] derEncoding = TlsUtilities.ReadOpaque24(input); + response = OcspResponse.GetInstance(TlsUtilities.ReadDerObject(derEncoding)); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new CertificateStatus(status_type, response); + } + + protected static bool IsCorrectType(byte statusType, object response) + { + switch (statusType) + { + case CertificateStatusType.ocsp: + return response is OcspResponse; + default: + throw new ArgumentException("unsupported value", "statusType"); + } + } + } +} diff --git a/crypto/src/crypto/tls/CertificateStatusRequest.cs b/crypto/src/crypto/tls/CertificateStatusRequest.cs new file mode 100644
index 000000000..9587d7df8 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateStatusRequest.cs
@@ -0,0 +1,95 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class CertificateStatusRequest + { + protected readonly byte mStatusType; + protected readonly object mRequest; + + public CertificateStatusRequest(byte statusType, Object request) + { + if (!IsCorrectType(statusType, request)) + throw new ArgumentException("not an instance of the correct type", "request"); + + this.mStatusType = statusType; + this.mRequest = request; + } + + public virtual byte StatusType + { + get { return mStatusType; } + } + + public virtual object Request + { + get { return mRequest; } + } + + public virtual OcspStatusRequest GetOcspStatusRequest() + { + if (!IsCorrectType(CertificateStatusType.ocsp, mRequest)) + throw new InvalidOperationException("'request' is not an OCSPStatusRequest"); + + return (OcspStatusRequest)mRequest; + } + + /** + * Encode this {@link CertificateStatusRequest} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mStatusType, output); + + switch (mStatusType) + { + case CertificateStatusType.ocsp: + ((OcspStatusRequest)mRequest).Encode(output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link CertificateStatusRequest} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateStatusRequest} object. + * @throws IOException + */ + public static CertificateStatusRequest Parse(Stream input) + { + byte status_type = TlsUtilities.ReadUint8(input); + object result; + + switch (status_type) + { + case CertificateStatusType.ocsp: + result = OcspStatusRequest.Parse(input); + break; + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new CertificateStatusRequest(status_type, result); + } + + protected static bool IsCorrectType(byte statusType, object request) + { + switch (statusType) + { + case CertificateStatusType.ocsp: + return request is OcspStatusRequest; + default: + throw new ArgumentException("unsupported value", "statusType"); + } + } + } +} diff --git a/crypto/src/crypto/tls/CertificateStatusType.cs b/crypto/src/crypto/tls/CertificateStatusType.cs
index 222d31635..54b741b42 100644 --- a/crypto/src/crypto/tls/CertificateStatusType.cs +++ b/crypto/src/crypto/tls/CertificateStatusType.cs
@@ -7,6 +7,6 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 3546 3.6 */ - public const short ocsp = 1; + public const byte ocsp = 1; } } diff --git a/crypto/src/crypto/tls/CertificateUrl.cs b/crypto/src/crypto/tls/CertificateUrl.cs new file mode 100644
index 000000000..a951b8063 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateUrl.cs
@@ -0,0 +1,124 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /* + * RFC 3546 3.3 + */ + public class CertificateUrl + { + protected readonly byte mType; + protected readonly IList mUrlAndHashList; + + /** + * @param type + * see {@link CertChainType} for valid constants. + * @param urlAndHashList + * a {@link IList} of {@link UrlAndHash}. + */ + public CertificateUrl(byte type, IList urlAndHashList) + { + if (!CertChainType.IsValid(type)) + throw new ArgumentException("not a valid CertChainType value", "type"); + if (urlAndHashList == null || urlAndHashList.Count < 1) + throw new ArgumentException("must have length > 0", "urlAndHashList"); + + this.mType = type; + this.mUrlAndHashList = urlAndHashList; + } + + /** + * @return {@link CertChainType} + */ + public virtual byte Type + { + get { return mType; } + } + + /** + * @return an {@link IList} of {@link UrlAndHash} + */ + public virtual IList UrlAndHashList + { + get { return mUrlAndHashList; } + } + + /** + * Encode this {@link CertificateUrl} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(this.mType, output); + + ListBuffer16 buf = new ListBuffer16(); + foreach (UrlAndHash urlAndHash in this.mUrlAndHashList) + { + urlAndHash.Encode(buf); + } + buf.EncodeTo(output); + } + + /** + * Parse a {@link CertificateUrl} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateUrl} object. + * @throws IOException + */ + public static CertificateUrl parse(TlsContext context, Stream input) + { + byte type = TlsUtilities.ReadUint8(input); + if (!CertChainType.IsValid(type)) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int totalLength = TlsUtilities.ReadUint16(input); + if (totalLength < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] urlAndHashListData = TlsUtilities.ReadFully(totalLength, input); + + MemoryStream buf = new MemoryStream(urlAndHashListData, false); + + IList url_and_hash_list = Platform.CreateArrayList(); + while (buf.Position < buf.Length) + { + UrlAndHash url_and_hash = UrlAndHash.Parse(context, buf); + url_and_hash_list.Add(url_and_hash); + } + + return new CertificateUrl(type, url_and_hash_list); + } + + // TODO Could be more generally useful + internal class ListBuffer16 + : MemoryStream + { + internal ListBuffer16() + { + // Reserve space for length + TlsUtilities.WriteUint16(0, this); + } + + internal void EncodeTo(Stream output) + { + // Patch actual length back in + long length = Length - 2; + TlsUtilities.CheckUint16(length); + this.Position = 0; + TlsUtilities.WriteUint16((int)length, this); + this.WriteTo(output); + this.Close(); + } + } + } +} diff --git a/crypto/src/crypto/tls/Chacha20Poly1305.cs b/crypto/src/crypto/tls/Chacha20Poly1305.cs new file mode 100644
index 000000000..e4e4c7ee2 --- /dev/null +++ b/crypto/src/crypto/tls/Chacha20Poly1305.cs
@@ -0,0 +1,153 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class Chacha20Poly1305 + : TlsCipher + { + protected readonly TlsContext context; + + protected readonly ChaChaEngine encryptCipher; + protected readonly ChaChaEngine decryptCipher; + + /// <exception cref="IOException"></exception> + public Chacha20Poly1305(TlsContext context) + { + if (!TlsUtilities.IsTlsV12(context)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.context = context; + + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, 64); + + KeyParameter client_write_key = new KeyParameter(key_block, 0, 32); + KeyParameter server_write_key = new KeyParameter(key_block, 32, 32); + + this.encryptCipher = new ChaChaEngine(20); + this.decryptCipher = new ChaChaEngine(20); + + KeyParameter encryptKey, decryptKey; + if (context.IsServer) + { + encryptKey = server_write_key; + decryptKey = client_write_key; + } + else + { + encryptKey = client_write_key; + decryptKey = server_write_key; + } + + byte[] dummyNonce = new byte[8]; + + this.encryptCipher.Init(true, new ParametersWithIV(encryptKey, dummyNonce)); + this.decryptCipher.Init(false, new ParametersWithIV(decryptKey, dummyNonce)); + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + return ciphertextLimit - 16; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + int ciphertextLength = len + 16; + + KeyParameter macKey = InitRecordMac(encryptCipher, true, seqNo); + + byte[] output = new byte[ciphertextLength]; + encryptCipher.ProcessBytes(plaintext, offset, len, output, 0); + + byte[] additionalData = GetAdditionalData(seqNo, type, len); + byte[] mac = CalculateRecordMac(macKey, additionalData, output, 0, len); + Array.Copy(mac, 0, output, len, mac.Length); + + return output; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + if (GetPlaintextLimit(len) < 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int plaintextLength = len - 16; + + byte[] receivedMAC = Arrays.CopyOfRange(ciphertext, offset + plaintextLength, offset + len); + + KeyParameter macKey = InitRecordMac(decryptCipher, false, seqNo); + + byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength); + byte[] calculatedMAC = CalculateRecordMac(macKey, additionalData, ciphertext, offset, plaintextLength); + + if (!Arrays.ConstantTimeAreEqual(calculatedMAC, receivedMAC)) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + + byte[] output = new byte[plaintextLength]; + decryptCipher.ProcessBytes(ciphertext, offset, plaintextLength, output, 0); + + return output; + } + + protected virtual KeyParameter InitRecordMac(ChaChaEngine cipher, bool forEncryption, long seqNo) + { + byte[] nonce = new byte[8]; + TlsUtilities.WriteUint64(seqNo, nonce, 0); + + cipher.Init(forEncryption, new ParametersWithIV(null, nonce)); + + byte[] firstBlock = new byte[64]; + cipher.ProcessBytes(firstBlock, 0, firstBlock.Length, firstBlock, 0); + + // NOTE: The BC implementation puts 'r' after 'k' + Array.Copy(firstBlock, 0, firstBlock, 32, 16); + KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); + Poly1305KeyGenerator.Clamp(macKey.GetKey()); + return macKey; + } + + protected virtual byte[] CalculateRecordMac(KeyParameter macKey, byte[] additionalData, byte[] buf, int off, int len) + { + IMac mac = new Poly1305(); + mac.Init(macKey); + + UpdateRecordMac(mac, additionalData, 0, additionalData.Length); + UpdateRecordMac(mac, buf, off, len); + return MacUtilities.DoFinal(mac); + } + + protected virtual void UpdateRecordMac(IMac mac, byte[] buf, int off, int len) + { + mac.BlockUpdate(buf, off, len); + + byte[] longLen = Pack.UInt64_To_LE((ulong)len); + mac.BlockUpdate(longLen, 0, longLen.Length); + } + + /// <exception cref="IOException"></exception> + protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len) + { + /* + * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + + * TLSCompressed.length + */ + byte[] additional_data = new byte[13]; + TlsUtilities.WriteUint64(seqNo, additional_data, 0); + TlsUtilities.WriteUint8(type, additional_data, 8); + TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9); + TlsUtilities.WriteUint16(len, additional_data, 11); + + return additional_data; + } + } +} diff --git a/crypto/src/crypto/tls/ChangeCipherSpec.cs b/crypto/src/crypto/tls/ChangeCipherSpec.cs
index 2ef4c3b34..323de9162 100644 --- a/crypto/src/crypto/tls/ChangeCipherSpec.cs +++ b/crypto/src/crypto/tls/ChangeCipherSpec.cs
@@ -4,6 +4,6 @@ namespace Org.BouncyCastle.Crypto.Tls { public abstract class ChangeCipherSpec { - public const short change_cipher_spec = 1; + public const byte change_cipher_spec = 1; } } diff --git a/crypto/src/crypto/tls/CipherSuite.cs b/crypto/src/crypto/tls/CipherSuite.cs
index 2c5077780..5ec36aee8 100644 --- a/crypto/src/crypto/tls/CipherSuite.cs +++ b/crypto/src/crypto/tls/CipherSuite.cs
@@ -56,7 +56,7 @@ namespace Org.BouncyCastle.Crypto.Tls public const int TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A; /* - * RFC 4132 + * RFC 5932 */ public const int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041; public const int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042; @@ -64,6 +64,7 @@ namespace Org.BouncyCastle.Crypto.Tls public const int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044; public const int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045; public const int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046; + public const int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084; public const int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085; public const int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086; @@ -71,6 +72,20 @@ namespace Org.BouncyCastle.Crypto.Tls public const int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088; public const int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089; + public const int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA; + public const int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB; + public const int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC; + public const int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD; + public const int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE; + public const int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF; + + public const int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0; + public const int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1; + public const int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2; + public const int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3; + public const int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4; + public const int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5; + /* * RFC 4162 */ @@ -240,6 +255,54 @@ namespace Org.BouncyCastle.Crypto.Tls public const int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF; /* + * RFC 6367 + */ + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC072; + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC073; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC074; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC075; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC076; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC077; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC078; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC079; + + public const int TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07A; + public const int TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07B; + public const int TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07C; + public const int TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07D; + public const int TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07E; + public const int TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07F; + public const int TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC080; + public const int TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC081; + public const int TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC082; + public const int TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC083; + public const int TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = 0xC084; + public const int TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = 0xC085; + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC086; + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC087; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC088; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC089; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08A; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08B; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08C; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08D; + + public const int TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08E; + public const int TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08F; + public const int TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC090; + public const int TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC091; + public const int TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC092; + public const int TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC093; + public const int TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC094; + public const int TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC095; + public const int TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC096; + public const int TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC097; + public const int TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC098; + public const int TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC099; + public const int TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC09A; + public const int TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC09B; + + /* * RFC 6655 */ public const int TLS_RSA_WITH_AES_128_CCM = 0xC09C; @@ -260,39 +323,55 @@ namespace Org.BouncyCastle.Crypto.Tls public const int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB; /* - * TBD[draft-josefsson-salsa20-tls-02] + * RFC 7251 + */ + public const int TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC; + public const int TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 0xC0AD; + public const int TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE; + public const int TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 0xC0AF; + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + public const int TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC13; + public const int TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC14; + public const int TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC15; + + /* + * draft-josefsson-salsa20-tls-04 */ - const int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF00; - const int TLS_RSA_WITH_SALSA20_SHA1 = 0xFF01; - const int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF02; - const int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xFF03; - const int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF04; - const int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xFF05; - const int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF06; - const int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xFF07; - const int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF08; - const int TLS_PSK_WITH_SALSA20_SHA1 = 0xFF09; - const int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0A; - const int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xFF0B; - const int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0C; - const int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xFF0D; - const int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0E; - const int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xFF0F; - const int TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF10; - const int TLS_RSA_WITH_SALSA20_UMAC96 = 0xFF11; - const int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF12; - const int TLS_DHE_RSA_WITH_SALSA20_UMAC96 = 0xFF13; - const int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF14; - const int TLS_ECDHE_RSA_WITH_SALSA20_UMAC96 = 0xFF15; - const int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF16; - const int TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96 = 0xFF17; - const int TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF18; - const int TLS_PSK_WITH_SALSA20_UMAC96 = 0xFF19; - const int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1A; - const int TLS_DHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1B; - const int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1C; - const int TLS_RSA_PSK_WITH_SALSA20_UMAC96 = 0xFF1D; - const int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1E; - const int TLS_ECDHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1F; + public const int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE410; + public const int TLS_RSA_WITH_SALSA20_SHA1 = 0xE411; + public const int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE412; + public const int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xE413; + public const int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE414; + public const int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xE415; + public const int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE416; + public const int TLS_PSK_WITH_SALSA20_SHA1 = 0xE417; + public const int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE418; + public const int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xE419; + public const int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE41A; + public const int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xE41B; + public const int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE41C; + public const int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xE41D; + public const int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE41E; + public const int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xE41F; + + /* + * draft-ietf-tls-downgrade-scsv-00 + */ + public const int TLS_FALLBACK_SCSV = 0x5600; + + public static bool IsScsv(int cipherSuite) + { + switch (cipherSuite) + { + case TLS_EMPTY_RENEGOTIATION_INFO_SCSV: + case TLS_FALLBACK_SCSV: + return true; + default: + return false; + } + } } } diff --git a/crypto/src/crypto/tls/ClientAuthenticationType.cs b/crypto/src/crypto/tls/ClientAuthenticationType.cs
index 51e6e5005..dd248f3df 100644 --- a/crypto/src/crypto/tls/ClientAuthenticationType.cs +++ b/crypto/src/crypto/tls/ClientAuthenticationType.cs
@@ -7,8 +7,8 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 5077 4 */ - public const short anonymous = 0; - public const short certificate_based = 1; - public const short psk = 2; + public const byte anonymous = 0; + public const byte certificate_based = 1; + public const byte psk = 2; } } diff --git a/crypto/src/crypto/tls/CombinedHash.cs b/crypto/src/crypto/tls/CombinedHash.cs
index 59ad87a7b..74a52d598 100644 --- a/crypto/src/crypto/tls/CombinedHash.cs +++ b/crypto/src/crypto/tls/CombinedHash.cs
@@ -1,82 +1,133 @@ using System; -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - /// <remarks>A combined hash, which implements md5(m) || sha1(m).</remarks> - internal class CombinedHash - : IDigest - { - private readonly MD5Digest md5; - private readonly Sha1Digest sha1; - - internal CombinedHash() - { - this.md5 = new MD5Digest(); - this.sha1 = new Sha1Digest(); - } - - internal CombinedHash(CombinedHash t) - { - this.md5 = new MD5Digest(t.md5); - this.sha1 = new Sha1Digest(t.sha1); - } - - /// <seealso cref="IDigest.AlgorithmName"/> - public string AlgorithmName - { - get - { - return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0"; - } - } - - /// <seealso cref="IDigest.GetByteLength"/> - public int GetByteLength() - { - return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength()); - } - - /// <seealso cref="IDigest.GetDigestSize"/> - public int GetDigestSize() - { - return md5.GetDigestSize() + sha1.GetDigestSize(); - } - - /// <seealso cref="IDigest.Update"/> - public void Update( - byte input) - { - md5.Update(input); - sha1.Update(input); - } - - /// <seealso cref="IDigest.BlockUpdate"/> - public void BlockUpdate( - byte[] input, - int inOff, - int len) - { - md5.BlockUpdate(input, inOff, len); - sha1.BlockUpdate(input, inOff, len); - } - - /// <seealso cref="IDigest.DoFinal"/> - public int DoFinal( - byte[] output, - int outOff) - { - int i1 = md5.DoFinal(output, outOff); - int i2 = sha1.DoFinal(output, outOff + i1); - return i1 + i2; - } - - /// <seealso cref="IDigest.Reset"/> - public void Reset() - { - md5.Reset(); - sha1.Reset(); - } - } + /** + * A combined hash, which implements md5(m) || sha1(m). + */ + internal class CombinedHash + : TlsHandshakeHash + { + protected TlsContext mContext; + protected IDigest mMd5; + protected IDigest mSha1; + + internal CombinedHash() + { + this.mMd5 = TlsUtilities.CreateHash(HashAlgorithm.md5); + this.mSha1 = TlsUtilities.CreateHash(HashAlgorithm.sha1); + } + + internal CombinedHash(CombinedHash t) + { + this.mContext = t.mContext; + this.mMd5 = TlsUtilities.CloneHash(HashAlgorithm.md5, t.mMd5); + this.mSha1 = TlsUtilities.CloneHash(HashAlgorithm.sha1, t.mSha1); + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual TlsHandshakeHash NotifyPrfDetermined() + { + return this; + } + + public virtual void TrackHashAlgorithm(byte hashAlgorithm) + { + throw new InvalidOperationException("CombinedHash only supports calculating the legacy PRF for handshake hash"); + } + + public virtual void SealHashAlgorithms() + { + } + + public virtual TlsHandshakeHash StopTracking() + { + return new CombinedHash(this); + } + + public virtual IDigest ForkPrfHash() + { + return new CombinedHash(this); + } + + public virtual byte[] GetFinalHash(byte hashAlgorithm) + { + throw new InvalidOperationException("CombinedHash doesn't support multiple hashes"); + } + + public virtual string AlgorithmName + { + get { return mMd5.AlgorithmName + " and " + mSha1.AlgorithmName; } + } + + public virtual int GetByteLength() + { + return System.Math.Max(mMd5.GetByteLength(), mSha1.GetByteLength()); + } + + public virtual int GetDigestSize() + { + return mMd5.GetDigestSize() + mSha1.GetDigestSize(); + } + + public virtual void Update(byte input) + { + mMd5.Update(input); + mSha1.Update(input); + } + + /** + * @see org.bouncycastle.crypto.Digest#update(byte[], int, int) + */ + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + mMd5.BlockUpdate(input, inOff, len); + mSha1.BlockUpdate(input, inOff, len); + } + + /** + * @see org.bouncycastle.crypto.Digest#doFinal(byte[], int) + */ + public virtual int DoFinal(byte[] output, int outOff) + { + if (mContext != null && TlsUtilities.IsSsl(mContext)) + { + Ssl3Complete(mMd5, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 48); + Ssl3Complete(mSha1, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 40); + } + + int i1 = mMd5.DoFinal(output, outOff); + int i2 = mSha1.DoFinal(output, outOff + i1); + return i1 + i2; + } + + /** + * @see org.bouncycastle.crypto.Digest#reset() + */ + public virtual void Reset() + { + mMd5.Reset(); + mSha1.Reset(); + } + + protected virtual void Ssl3Complete(IDigest d, byte[] ipad, byte[] opad, int padLength) + { + byte[] master_secret = mContext.SecurityParameters.masterSecret; + + d.BlockUpdate(master_secret, 0, master_secret.Length); + d.BlockUpdate(ipad, 0, padLength); + + byte[] tmp = DigestUtilities.DoFinal(d); + + d.BlockUpdate(master_secret, 0, master_secret.Length); + d.BlockUpdate(opad, 0, padLength); + d.BlockUpdate(tmp, 0, tmp.Length); + } + } } diff --git a/crypto/src/crypto/tls/CompressionMethod.cs b/crypto/src/crypto/tls/CompressionMethod.cs
index e4ee9666f..89c1f5ff4 100644 --- a/crypto/src/crypto/tls/CompressionMethod.cs +++ b/crypto/src/crypto/tls/CompressionMethod.cs
@@ -7,7 +7,7 @@ namespace Org.BouncyCastle.Crypto.Tls /// </summary> public abstract class CompressionMethod { - public const byte NULL = 0; + public const byte cls_null = 0; /* * RFC 3749 2 diff --git a/crypto/src/crypto/tls/DatagramTransport.cs b/crypto/src/crypto/tls/DatagramTransport.cs new file mode 100644
index 000000000..524a8b181 --- /dev/null +++ b/crypto/src/crypto/tls/DatagramTransport.cs
@@ -0,0 +1,23 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface DatagramTransport + { + /// <exception cref="IOException"/> + int GetReceiveLimit(); + + /// <exception cref="IOException"/> + int GetSendLimit(); + + /// <exception cref="IOException"/> + int Receive(byte[] buf, int off, int len, int waitMillis); + + /// <exception cref="IOException"/> + void Send(byte[] buf, int off, int len); + + /// <exception cref="IOException"/> + void Close(); + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
index 2bd2f40bf..5147a1990 100644 --- a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs +++ b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.Parameters; @@ -8,69 +9,61 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { public class DefaultTlsAgreementCredentials - : TlsAgreementCredentials + : AbstractTlsAgreementCredentials { - protected Certificate clientCert; - protected AsymmetricKeyParameter clientPrivateKey; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; - protected IBasicAgreement basicAgreement; - protected bool truncateAgreement; + protected readonly IBasicAgreement mBasicAgreement; + protected readonly bool mTruncateAgreement; - public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey) + public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey) { - if (clientCertificate == null) - { - throw new ArgumentNullException("clientCertificate"); - } - if (clientCertificate.Length == 0) - { - throw new ArgumentException("cannot be empty", "clientCertificate"); - } - if (clientPrivateKey == null) - { - throw new ArgumentNullException("clientPrivateKey"); - } - if (!clientPrivateKey.IsPrivate) - { - throw new ArgumentException("must be private", "clientPrivateKey"); - } + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "certificate"); + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); - if (clientPrivateKey is DHPrivateKeyParameters) + if (privateKey is DHPrivateKeyParameters) { - basicAgreement = new DHBasicAgreement(); - truncateAgreement = true; + mBasicAgreement = new DHBasicAgreement(); + mTruncateAgreement = true; } - else if (clientPrivateKey is ECPrivateKeyParameters) + else if (privateKey is ECPrivateKeyParameters) { - basicAgreement = new ECDHBasicAgreement(); - truncateAgreement = false; + mBasicAgreement = new ECDHBasicAgreement(); + mTruncateAgreement = false; } else { - throw new ArgumentException("type not supported: " - + clientPrivateKey.GetType().FullName, "clientPrivateKey"); + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); } - this.clientCert = clientCertificate; - this.clientPrivateKey = clientPrivateKey; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; } - public virtual Certificate Certificate + public override Certificate Certificate { - get { return clientCert; } + get { return mCertificate; } } - public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey) + /// <exception cref="IOException"></exception> + public override byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey) { - basicAgreement.Init(clientPrivateKey); - BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey); + mBasicAgreement.Init(mPrivateKey); + BigInteger agreementValue = mBasicAgreement.CalculateAgreement(peerPublicKey); - if (truncateAgreement) + if (mTruncateAgreement) { return BigIntegers.AsUnsignedByteArray(agreementValue); } - return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue); + return BigIntegers.AsUnsignedByteArray(mBasicAgreement.GetFieldSize(), agreementValue); } } } diff --git a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
index 18b23a67b..7c4213c25 100644 --- a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs +++ b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -1,53 +1,146 @@ 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 { public class DefaultTlsCipherFactory - : TlsCipherFactory + : AbstractTlsCipherFactory { - public virtual TlsCipher CreateCipher(TlsClientContext context, - int encryptionAlgorithm, DigestAlgorithm digestAlgorithm) + /// <exception cref="IOException"></exception> + public override TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) { switch (encryptionAlgorithm) { - case EncryptionAlgorithm.cls_3DES_EDE_CBC: - return CreateDesEdeCipher(context, 24, digestAlgorithm); - case EncryptionAlgorithm.AES_128_CBC: - return CreateAesCipher(context, 16, digestAlgorithm); - case EncryptionAlgorithm.AES_256_CBC: - return CreateAesCipher(context, 32, digestAlgorithm); - case EncryptionAlgorithm.RC4_128: - return CreateRC4Cipher(context, 16, digestAlgorithm); - default: - throw new TlsFatalAlert(AlertDescription.internal_error); + case EncryptionAlgorithm.cls_3DES_EDE_CBC: + return CreateDesEdeCipher(context, macAlgorithm); + case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305: + // NOTE: Ignores macAlgorithm + return CreateChaCha20Poly1305(context); + case EncryptionAlgorithm.AES_128_CBC: + return CreateAESCipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.AES_128_CCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 16, 16); + case EncryptionAlgorithm.AES_128_CCM_8: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 16, 8); + case EncryptionAlgorithm.AES_256_CCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 32, 16); + case EncryptionAlgorithm.AES_256_CCM_8: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 32, 8); + case EncryptionAlgorithm.AES_128_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Gcm(context, 16, 16); + case EncryptionAlgorithm.AES_256_CBC: + return CreateAESCipher(context, 32, macAlgorithm); + case EncryptionAlgorithm.AES_256_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Gcm(context, 32, 16); + case EncryptionAlgorithm.CAMELLIA_128_CBC: + return CreateCamelliaCipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.CAMELLIA_128_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Camellia_Gcm(context, 16, 16); + case EncryptionAlgorithm.CAMELLIA_256_CBC: + return CreateCamelliaCipher(context, 32, macAlgorithm); + case EncryptionAlgorithm.CAMELLIA_256_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Camellia_Gcm(context, 32, 16); + case EncryptionAlgorithm.ESTREAM_SALSA20: + return CreateSalsa20Cipher(context, 12, 32, macAlgorithm); + case EncryptionAlgorithm.NULL: + return CreateNullCipher(context, macAlgorithm); + case EncryptionAlgorithm.RC4_128: + return CreateRC4Cipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.SALSA20: + return CreateSalsa20Cipher(context, 20, 32, macAlgorithm); + case EncryptionAlgorithm.SEED_CBC: + return CreateSeedCipher(context, macAlgorithm); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); } } /// <exception cref="IOException"></exception> - protected virtual TlsCipher CreateRC4Cipher(TlsClientContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm) + protected virtual TlsBlockCipher CreateAESCipher(TlsContext context, int cipherKeySize, int macAlgorithm) { - return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize); + return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize); } /// <exception cref="IOException"></exception> - protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize, - DigestAlgorithm digestAlgorithm) + protected virtual TlsBlockCipher CreateCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm) { - return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(), - CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize); + return new TlsBlockCipher(context, CreateCamelliaBlockCipher(), + CreateCamelliaBlockCipher(), CreateHMacDigest(macAlgorithm), + CreateHMacDigest(macAlgorithm), cipherKeySize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsCipher CreateChaCha20Poly1305(TlsContext context) + { + return new Chacha20Poly1305(context); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsContext context, int cipherKeySize, int macSize) + { + return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Ccm(), + CreateAeadBlockCipher_Aes_Ccm(), cipherKeySize, macSize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsContext context, int cipherKeySize, int macSize) + { + return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Gcm(), + CreateAeadBlockCipher_Aes_Gcm(), cipherKeySize, macSize); } /// <exception cref="IOException"></exception> - protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize, - DigestAlgorithm digestAlgorithm) + protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsContext context, int cipherKeySize, int macSize) + { + return new TlsAeadCipher(context, CreateAeadBlockCipher_Camellia_Gcm(), + CreateAeadBlockCipher_Camellia_Gcm(), cipherKeySize, macSize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsBlockCipher CreateDesEdeCipher(TlsContext context, int macAlgorithm) { return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(), - CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize); + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 24); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsNullCipher CreateNullCipher(TlsContext context, int macAlgorithm) + { + return new TlsNullCipher(context, CreateHMacDigest(macAlgorithm), + CreateHMacDigest(macAlgorithm)); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsStreamCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm) + { + return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, false); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsStreamCipher CreateSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm) + { + return new TlsStreamCipher(context, CreateSalsa20StreamCipher(rounds), CreateSalsa20StreamCipher(rounds), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, true); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsBlockCipher CreateSeedCipher(TlsContext context, int macAlgorithm) + { + return new TlsBlockCipher(context, CreateSeedBlockCipher(), CreateSeedBlockCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 16); } protected virtual IBlockCipher CreateAesEngine() @@ -55,11 +148,38 @@ namespace Org.BouncyCastle.Crypto.Tls return new AesEngine(); } + protected virtual IBlockCipher CreateCamelliaEngine() + { + return new CamelliaEngine(); + } + protected virtual IBlockCipher CreateAesBlockCipher() { return new CbcBlockCipher(CreateAesEngine()); } + protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm() + { + return new CcmBlockCipher(CreateAesEngine()); + } + + protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm() + { + // TODO Consider allowing custom configuration of multiplier + return new GcmBlockCipher(CreateAesEngine()); + } + + protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm() + { + // TODO Consider allowing custom configuration of multiplier + return new GcmBlockCipher(CreateCamelliaEngine()); + } + + protected virtual IBlockCipher CreateCamelliaBlockCipher() + { + return new CbcBlockCipher(CreateCamelliaEngine()); + } + protected virtual IBlockCipher CreateDesEdeBlockCipher() { return new CbcBlockCipher(new DesEdeEngine()); @@ -70,21 +190,35 @@ namespace Org.BouncyCastle.Crypto.Tls return new RC4Engine(); } + protected virtual IStreamCipher CreateSalsa20StreamCipher(int rounds) + { + return new Salsa20Engine(rounds); + } + + protected virtual IBlockCipher CreateSeedBlockCipher() + { + return new CbcBlockCipher(new SeedEngine()); + } + /// <exception cref="IOException"></exception> - protected virtual IDigest CreateDigest(DigestAlgorithm digestAlgorithm) + protected virtual IDigest CreateHMacDigest(int macAlgorithm) { - switch (digestAlgorithm) + switch (macAlgorithm) { - case DigestAlgorithm.MD5: - return new MD5Digest(); - case DigestAlgorithm.SHA: - return new Sha1Digest(); - case DigestAlgorithm.SHA256: - return new Sha256Digest(); - case DigestAlgorithm.SHA384: - return new Sha384Digest(); - default: - throw new TlsFatalAlert(AlertDescription.internal_error); + case MacAlgorithm.cls_null: + return null; + case MacAlgorithm.hmac_md5: + return TlsUtilities.CreateHash(HashAlgorithm.md5); + case MacAlgorithm.hmac_sha1: + return TlsUtilities.CreateHash(HashAlgorithm.sha1); + case MacAlgorithm.hmac_sha256: + return TlsUtilities.CreateHash(HashAlgorithm.sha256); + case MacAlgorithm.hmac_sha384: + return TlsUtilities.CreateHash(HashAlgorithm.sha384); + case MacAlgorithm.hmac_sha512: + return TlsUtilities.CreateHash(HashAlgorithm.sha512); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); } } } diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index d59fae164..ff7e6da85 100644 --- a/crypto/src/crypto/tls/DefaultTlsClient.cs +++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -12,272 +12,100 @@ using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Tls { public abstract class DefaultTlsClient - : TlsClient + : AbstractTlsClient { - protected TlsCipherFactory cipherFactory; - - protected TlsClientContext context; - - protected byte selectedCompressionMethod; - protected int selectedCipherSuite; - public DefaultTlsClient() - : this(new DefaultTlsCipherFactory()) + : base() { } public DefaultTlsClient(TlsCipherFactory cipherFactory) + : base(cipherFactory) { - this.cipherFactory = cipherFactory; - } - - public virtual void Init(TlsClientContext context) - { - this.context = context; } - public virtual bool ShouldUseGmtUnixTime() + public override int[] GetCipherSuites() { - /* - * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that - * TLS implementors MUST by default set the entire value the ClientHello.Random and - * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random - * sequence. - */ - return false; - } - - public virtual int[] GetCipherSuites() - { - return new int[] { - CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + return new int[] + { + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_RSA_WITH_RC4_128_SHA, }; } - public virtual byte[] GetCompressionMethods() - { - /* - * To offer DEFLATE compression, override this method: - * return new byte[] { CompressionMethod.DEFLATE, CompressionMethod.NULL }; - */ - - return new byte[] { CompressionMethod.NULL }; - } - - public virtual IDictionary GetClientExtensions() - { - return null; - } - - public virtual void NotifySessionID(byte[] sessionID) + public override TlsKeyExchange GetKeyExchange() { - // Currently ignored - } - - public virtual void NotifySelectedCipherSuite(int selectedCipherSuite) - { - this.selectedCipherSuite = selectedCipherSuite; - } + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); - public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod) - { - this.selectedCompressionMethod = selectedCompressionMethod; - } - - public virtual void NotifySecureRenegotiation(bool secureRenegotiation) - { - if (!secureRenegotiation) + switch (keyExchangeAlgorithm) { - /* - * RFC 5746 3.4. - * If the extension is not present, the server does not support - * secure renegotiation; set secure_renegotiation flag to FALSE. - * In this case, some clients may want to terminate the handshake - * instead of continuing; see Section 4.1 for discussion. - */ -// throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - } + case KeyExchangeAlgorithm.DH_DSS: + case KeyExchangeAlgorithm.DH_RSA: + return CreateDHKeyExchange(keyExchangeAlgorithm); - public virtual void ProcessServerExtensions(IDictionary serverExtensions) - { - } - - public virtual TlsKeyExchange GetKeyExchange() - { - switch (selectedCipherSuite) - { - case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: - return CreateRsaKeyExchange(); - - case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: - return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS); - - case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: - return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA); - - case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: - return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS); + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.DHE_RSA: + return CreateDheKeyExchange(keyExchangeAlgorithm); - case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA); + case KeyExchangeAlgorithm.ECDH_ECDSA: + case KeyExchangeAlgorithm.ECDH_RSA: + return CreateECDHKeyExchange(keyExchangeAlgorithm); - case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: - return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA); - - case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: - return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA); - - case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: - return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA); - - case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: - return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected cipher suite was in the list of client-offered cipher - * suites, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - } + case KeyExchangeAlgorithm.ECDHE_ECDSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + return CreateECDheKeyExchange(keyExchangeAlgorithm); - public abstract TlsAuthentication GetAuthentication(); + case KeyExchangeAlgorithm.RSA: + return CreateRsaKeyExchange(); - public virtual TlsCompression GetCompression() - { - switch (selectedCompressionMethod) - { - case CompressionMethod.NULL: - return new TlsNullCompression(); - - case CompressionMethod.DEFLATE: - return new TlsDeflateCompression(); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected compression method was in the list of client-offered compression - * methods, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - } - - public virtual TlsCipher GetCipher() - { - switch (selectedCipherSuite) - { - case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA); - - case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, DigestAlgorithm.SHA); - - case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA); - - case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected cipher suite was in the list of client-offered cipher - * suites, so if we now can't produce an implementation, we shouldn't have - * offered it! + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! */ - throw new TlsFatalAlert(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); } } protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange) { - return new TlsDHKeyExchange(context, keyExchange); + return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null); } protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange) { - return new TlsDheKeyExchange(context, keyExchange); + return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null); } protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange) { - return new TlsECDHKeyExchange(context, keyExchange); + return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); } protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange) { - return new TlsECDheKeyExchange(context, keyExchange); + return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); } protected virtual TlsKeyExchange CreateRsaKeyExchange() { - return new TlsRsaKeyExchange(context); + return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms); } } } diff --git a/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs new file mode 100644
index 000000000..34d15d146 --- /dev/null +++ b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs
@@ -0,0 +1,51 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsEncryptionCredentials + : AbstractTlsEncryptionCredentials + { + protected readonly TlsContext mContext; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; + + public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate, + AsymmetricKeyParameter privateKey) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "certificate"); + if (privateKey == null) + throw new ArgumentNullException("'privateKey' cannot be null"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); + + if (privateKey is RsaKeyParameters) + { + } + else + { + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); + } + + this.mContext = context; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + } + + public override Certificate Certificate + { + get { return mCertificate; } + } + + /// <exception cref="IOException"></exception> + public override byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret) + { + return TlsRsaUtilities.SafeDecryptPreMasterSecret(mContext, (RsaKeyParameters)mPrivateKey, encryptedPreMasterSecret); + } + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsServer.cs b/crypto/src/crypto/tls/DefaultTlsServer.cs new file mode 100644
index 000000000..77cd5f1cc --- /dev/null +++ b/crypto/src/crypto/tls/DefaultTlsServer.cs
@@ -0,0 +1,162 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class DefaultTlsServer + : AbstractTlsServer + { + public DefaultTlsServer() + : base() + { + } + + public DefaultTlsServer(TlsCipherFactory cipherFactory) + : base(cipherFactory) + { + } + + protected virtual TlsSignerCredentials GetDsaSignerCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual TlsSignerCredentials GetECDsaSignerCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual TlsSignerCredentials GetRsaSignerCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual DHParameters GetDHParameters() + { + return DHStandardGroups.rfc5114_2048_256; + } + + protected override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + }; + } + + public override TlsCredentials GetCredentials() + { + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); + + switch (keyExchangeAlgorithm) + { + case KeyExchangeAlgorithm.DH_DSS: + case KeyExchangeAlgorithm.DHE_DSS: + return GetDsaSignerCredentials(); + + case KeyExchangeAlgorithm.ECDH_ECDSA: + case KeyExchangeAlgorithm.ECDHE_ECDSA: + return GetECDsaSignerCredentials(); + + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + return GetRsaSignerCredentials(); + + case KeyExchangeAlgorithm.RSA: + return GetRsaEncryptionCredentials(); + + default: + /* Note: internal error here; selected a key exchange we don't implement! */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsKeyExchange GetKeyExchange() + { + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); + + switch (keyExchangeAlgorithm) + { + case KeyExchangeAlgorithm.DH_DSS: + case KeyExchangeAlgorithm.DH_RSA: + return CreateDHKeyExchange(keyExchangeAlgorithm); + + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.DHE_RSA: + return CreateDheKeyExchange(keyExchangeAlgorithm); + + case KeyExchangeAlgorithm.ECDH_ECDSA: + case KeyExchangeAlgorithm.ECDH_RSA: + return CreateECDHKeyExchange(keyExchangeAlgorithm); + + case KeyExchangeAlgorithm.ECDHE_ECDSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + return CreateECDheKeyExchange(keyExchangeAlgorithm); + + case KeyExchangeAlgorithm.RSA: + return CreateRsaKeyExchange(); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange) + { + return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters()); + } + + protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange) + { + return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters()); + } + + protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange) + { + return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); + } + + protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange) + { + return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); + } + + protected virtual TlsKeyExchange CreateRsaKeyExchange() + { + return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms); + } + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
index 2c5aa3524..c7a136573 100644 --- a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs +++ b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -1,76 +1,92 @@ using System; +using System.IO; using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Tls { public class DefaultTlsSignerCredentials - : TlsSignerCredentials + : AbstractTlsSignerCredentials { - protected TlsClientContext context; - protected Certificate clientCert; - protected AsymmetricKeyParameter clientPrivateKey; + protected readonly TlsContext mContext; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; + protected readonly SignatureAndHashAlgorithm mSignatureAndHashAlgorithm; - protected TlsSigner clientSigner; + protected readonly TlsSigner mSigner; - public DefaultTlsSignerCredentials(TlsClientContext context, - Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey) + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey) + : this(context, certificate, privateKey, null) { - if (clientCertificate == null) - { - throw new ArgumentNullException("clientCertificate"); - } - if (clientCertificate.Length == 0) - { + } + + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) throw new ArgumentException("cannot be empty", "clientCertificate"); - } - if (clientPrivateKey == null) - { - throw new ArgumentNullException("clientPrivateKey"); - } - if (!clientPrivateKey.IsPrivate) - { - throw new ArgumentException("must be private", "clientPrivateKey"); - } + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); + if (TlsUtilities.IsTlsV12(context) && signatureAndHashAlgorithm == null) + throw new ArgumentException("cannot be null for (D)TLS 1.2+", "signatureAndHashAlgorithm"); - if (clientPrivateKey is RsaKeyParameters) + if (privateKey is RsaKeyParameters) { - clientSigner = new TlsRsaSigner(); + mSigner = new TlsRsaSigner(); } - else if (clientPrivateKey is DsaPrivateKeyParameters) + else if (privateKey is DsaPrivateKeyParameters) { - clientSigner = new TlsDssSigner(); + mSigner = new TlsDssSigner(); } - else if (clientPrivateKey is ECPrivateKeyParameters) + else if (privateKey is ECPrivateKeyParameters) { - clientSigner = new TlsECDsaSigner(); + mSigner = new TlsECDsaSigner(); } else { - throw new ArgumentException("type not supported: " - + clientPrivateKey.GetType().FullName, "clientPrivateKey"); + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); } - this.context = context; - this.clientCert = clientCertificate; - this.clientPrivateKey = clientPrivateKey; + this.mSigner.Init(context); + + this.mContext = context; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + this.mSignatureAndHashAlgorithm = signatureAndHashAlgorithm; } - public virtual Certificate Certificate + public override Certificate Certificate { - get { return clientCert; } + get { return mCertificate; } } - public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1) + /// <exception cref="IOException"></exception> + public override byte[] GenerateCertificateSignature(byte[] hash) { try { - return clientSigner.GenerateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1); + if (TlsUtilities.IsTlsV12(mContext)) + { + return mSigner.GenerateRawSignature(mSignatureAndHashAlgorithm, mPrivateKey, hash); + } + else + { + return mSigner.GenerateRawSignature(mPrivateKey, hash); + } } - catch (CryptoException) + catch (CryptoException e) { - throw new TlsFatalAlert(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); } } + + public override SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get { return mSignatureAndHashAlgorithm; } + } } } diff --git a/crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs b/crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs new file mode 100644
index 000000000..cc933bff9 --- /dev/null +++ b/crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs
@@ -0,0 +1,70 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Agreement.Srp; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsSrpGroupVerifier + : TlsSrpGroupVerifier + { + protected static readonly IList DefaultGroups = Platform.CreateArrayList(); + + static DefaultTlsSrpGroupVerifier() + { + DefaultGroups.Add(Srp6StandardGroups.rfc5054_1024); + DefaultGroups.Add(Srp6StandardGroups.rfc5054_1536); + DefaultGroups.Add(Srp6StandardGroups.rfc5054_2048); + DefaultGroups.Add(Srp6StandardGroups.rfc5054_3072); + DefaultGroups.Add(Srp6StandardGroups.rfc5054_4096); + DefaultGroups.Add(Srp6StandardGroups.rfc5054_6144); + DefaultGroups.Add(Srp6StandardGroups.rfc5054_8192); + } + + // Vector is (SRP6GroupParameters) + protected readonly IList mGroups; + + /** + * Accept only the group parameters specified in RFC 5054 Appendix A. + */ + public DefaultTlsSrpGroupVerifier() + : this(DefaultGroups) + { + } + + /** + * Specify a custom set of acceptable group parameters. + * + * @param groups a {@link Vector} of acceptable {@link SRP6GroupParameters} + */ + public DefaultTlsSrpGroupVerifier(IList groups) + { + this.mGroups = groups; + } + + public virtual bool Accept(Srp6GroupParameters group) + { + foreach (Srp6GroupParameters entry in mGroups) + { + if (AreGroupsEqual(group, entry)) + { + return true; + } + } + return false; + } + + protected virtual bool AreGroupsEqual(Srp6GroupParameters a, Srp6GroupParameters b) + { + return a == b || (AreParametersEqual(a.N, b.N) && AreParametersEqual(a.G, b.G)); + } + + protected virtual bool AreParametersEqual(BigInteger a, BigInteger b) + { + return a == b || a.Equals(b); + } + } +} diff --git a/crypto/src/crypto/tls/DeferredHash.cs b/crypto/src/crypto/tls/DeferredHash.cs new file mode 100644
index 000000000..1112d4a3c --- /dev/null +++ b/crypto/src/crypto/tls/DeferredHash.cs
@@ -0,0 +1,201 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * Buffers input until the hash algorithm is determined. + */ + internal class DeferredHash + : TlsHandshakeHash + { + protected const int BUFFERING_HASH_LIMIT = 4; + + protected TlsContext mContext; + + private DigestInputBuffer mBuf; + private IDictionary mHashes; + private int mPrfHashAlgorithm; + + internal DeferredHash() + { + this.mBuf = new DigestInputBuffer(); + this.mHashes = Platform.CreateHashtable(); + this.mPrfHashAlgorithm = -1; + } + + private DeferredHash(byte prfHashAlgorithm, IDigest prfHash) + { + this.mBuf = null; + this.mHashes = Platform.CreateHashtable(); + this.mPrfHashAlgorithm = prfHashAlgorithm; + mHashes[prfHashAlgorithm] = prfHash; + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual TlsHandshakeHash NotifyPrfDetermined() + { + int prfAlgorithm = mContext.SecurityParameters.PrfAlgorithm; + if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy) + { + CombinedHash legacyHash = new CombinedHash(); + legacyHash.Init(mContext); + mBuf.UpdateDigest(legacyHash); + return legacyHash.NotifyPrfDetermined(); + } + + this.mPrfHashAlgorithm = TlsUtilities.GetHashAlgorithmForPrfAlgorithm(prfAlgorithm); + + CheckTrackingHash((byte)mPrfHashAlgorithm); + + return this; + } + + public virtual void TrackHashAlgorithm(byte hashAlgorithm) + { + if (mBuf == null) + throw new InvalidOperationException("Too late to track more hash algorithms"); + + CheckTrackingHash(hashAlgorithm); + } + + public virtual void SealHashAlgorithms() + { + CheckStopBuffering(); + } + + public virtual TlsHandshakeHash StopTracking() + { + byte prfHashAlgorithm = (byte)mPrfHashAlgorithm; + IDigest prfHash = TlsUtilities.CloneHash(prfHashAlgorithm, (IDigest)mHashes[prfHashAlgorithm]); + if (mBuf != null) + { + mBuf.UpdateDigest(prfHash); + } + DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash); + result.Init(mContext); + return result; + } + + public virtual IDigest ForkPrfHash() + { + CheckStopBuffering(); + + byte prfHashAlgorithm = (byte)mPrfHashAlgorithm; + if (mBuf != null) + { + IDigest prfHash = TlsUtilities.CreateHash(prfHashAlgorithm); + mBuf.UpdateDigest(prfHash); + return prfHash; + } + + return TlsUtilities.CloneHash(prfHashAlgorithm, (IDigest)mHashes[prfHashAlgorithm]); + } + + public virtual byte[] GetFinalHash(byte hashAlgorithm) + { + IDigest d = (IDigest)mHashes[hashAlgorithm]; + if (d == null) + throw new InvalidOperationException("HashAlgorithm " + hashAlgorithm + " is not being tracked"); + + d = TlsUtilities.CloneHash(hashAlgorithm, d); + if (mBuf != null) + { + mBuf.UpdateDigest(d); + } + + return DigestUtilities.DoFinal(d); + } + + public virtual string AlgorithmName + { + get { throw new InvalidOperationException("Use Fork() to get a definite IDigest"); } + } + + public virtual int GetByteLength() + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual int GetDigestSize() + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual void Update(byte input) + { + if (mBuf != null) + { + mBuf.WriteByte(input); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.Update(input); + } + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + if (mBuf != null) + { + mBuf.Write(input, inOff, len); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.BlockUpdate(input, inOff, len); + } + } + + public virtual int DoFinal(byte[] output, int outOff) + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual void Reset() + { + if (mBuf != null) + { + mBuf.SetLength(0); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.Reset(); + } + } + + protected virtual void CheckStopBuffering() + { + if (mBuf != null && mHashes.Count <= BUFFERING_HASH_LIMIT) + { + foreach (IDigest hash in mHashes.Values) + { + mBuf.UpdateDigest(hash); + } + + this.mBuf = null; + } + } + + protected virtual void CheckTrackingHash(byte hashAlgorithm) + { + if (!mHashes.Contains(hashAlgorithm)) + { + IDigest hash = TlsUtilities.CreateHash(hashAlgorithm); + mHashes[hashAlgorithm] = hash; + } + } + } +} diff --git a/crypto/src/crypto/tls/DigestAlgorithm.cs b/crypto/src/crypto/tls/DigestAlgorithm.cs deleted file mode 100644
index 745bea448..000000000 --- a/crypto/src/crypto/tls/DigestAlgorithm.cs +++ /dev/null
@@ -1,24 +0,0 @@ -using System; - -namespace Org.BouncyCastle.Crypto.Tls -{ - /// <summary>RFC 2246</summary> - /// <remarks> - /// Note that the values here are implementation-specific and arbitrary. It is recommended not to - /// depend on the particular values (e.g. serialization). - /// </remarks> - [Obsolete("Use MacAlgorithm constants instead")] - public enum DigestAlgorithm - { - NULL, - MD5, - SHA, - - /* - * RFC 5246 - */ - SHA256, - SHA384, - SHA512, - } -} diff --git a/crypto/src/crypto/tls/DigitallySigned.cs b/crypto/src/crypto/tls/DigitallySigned.cs new file mode 100644
index 000000000..8b7344fd9 --- /dev/null +++ b/crypto/src/crypto/tls/DigitallySigned.cs
@@ -0,0 +1,70 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DigitallySigned + { + protected readonly SignatureAndHashAlgorithm mAlgorithm; + protected readonly byte[] mSignature; + + public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature) + { + if (signature == null) + throw new ArgumentNullException("signature"); + + this.mAlgorithm = algorithm; + this.mSignature = signature; + } + + /** + * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2). + */ + public virtual SignatureAndHashAlgorithm Algorithm + { + get { return mAlgorithm; } + } + + public virtual byte[] Signature + { + get { return mSignature; } + } + + /** + * Encode this {@link DigitallySigned} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + if (mAlgorithm != null) + { + mAlgorithm.Encode(output); + } + TlsUtilities.WriteOpaque16(mSignature, output); + } + + /** + * Parse a {@link DigitallySigned} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link DigitallySigned} object. + * @throws IOException + */ + public static DigitallySigned Parse(TlsContext context, Stream input) + { + SignatureAndHashAlgorithm algorithm = null; + if (TlsUtilities.IsTlsV12(context)) + { + algorithm = SignatureAndHashAlgorithm.Parse(input); + } + byte[] signature = TlsUtilities.ReadOpaque16(input); + return new DigitallySigned(algorithm, signature); + } + } +} diff --git a/crypto/src/crypto/tls/DtlsClientProtocol.cs b/crypto/src/crypto/tls/DtlsClientProtocol.cs new file mode 100644
index 000000000..411e7cca2 --- /dev/null +++ b/crypto/src/crypto/tls/DtlsClientProtocol.cs
@@ -0,0 +1,839 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DtlsClientProtocol + : DtlsProtocol + { + public DtlsClientProtocol(SecureRandom secureRandom) + : base(secureRandom) + { + } + + public virtual DtlsTransport Connect(TlsClient client, DatagramTransport transport) + { + if (client == null) + throw new ArgumentNullException("client"); + if (transport == null) + throw new ArgumentNullException("transport"); + + SecurityParameters securityParameters = new SecurityParameters(); + securityParameters.entity = ConnectionEnd.client; + + ClientHandshakeState state = new ClientHandshakeState(); + state.client = client; + state.clientContext = new TlsClientContextImpl(mSecureRandom, securityParameters); + + securityParameters.clientRandom = TlsProtocol.CreateRandomBlock(client.ShouldUseGmtUnixTime(), + state.clientContext.NonceRandomGenerator); + + client.Init(state.clientContext); + + DtlsRecordLayer recordLayer = new DtlsRecordLayer(transport, state.clientContext, client, ContentType.handshake); + + TlsSession sessionToResume = state.client.GetSessionToResume(); + if (sessionToResume != null && sessionToResume.IsResumable) + { + SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); + if (sessionParameters != null) + { + state.tlsSession = sessionToResume; + state.sessionParameters = sessionParameters; + } + } + + try + { + return ClientHandshake(state, recordLayer); + } + catch (TlsFatalAlert fatalAlert) + { + recordLayer.Fail(fatalAlert.AlertDescription); + throw fatalAlert; + } + catch (IOException e) + { + recordLayer.Fail(AlertDescription.internal_error); + throw e; + } + catch (Exception e) + { + recordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + internal virtual DtlsTransport ClientHandshake(ClientHandshakeState state, DtlsRecordLayer recordLayer) + { + SecurityParameters securityParameters = state.clientContext.SecurityParameters; + DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.clientContext, recordLayer); + + byte[] clientHelloBody = GenerateClientHello(state, state.client); + handshake.SendMessage(HandshakeType.client_hello, clientHelloBody); + + DtlsReliableHandshake.Message serverMessage = handshake.ReceiveMessage(); + + while (serverMessage.Type == HandshakeType.hello_verify_request) + { + ProtocolVersion recordLayerVersion = recordLayer.ResetDiscoveredPeerVersion(); + ProtocolVersion client_version = state.clientContext.ClientVersion; + + /* + * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0 regardless of + * the version of TLS that is expected to be negotiated. DTLS 1.2 and 1.0 clients MUST use + * the version solely to indicate packet formatting (which is the same in both DTLS 1.2 and + * 1.0) and not as part of version negotiation. + */ + if (!recordLayerVersion.IsEqualOrEarlierVersionOf(client_version)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + byte[] cookie = ProcessHelloVerifyRequest(state, serverMessage.Body); + byte[] patched = PatchClientHelloWithCookie(clientHelloBody, cookie); + + handshake.ResetHandshakeMessagesDigest(); + handshake.SendMessage(HandshakeType.client_hello, patched); + + serverMessage = handshake.ReceiveMessage(); + } + + if (serverMessage.Type == HandshakeType.server_hello) + { + ReportServerVersion(state, recordLayer.DiscoveredPeerVersion); + + ProcessServerHello(state, serverMessage.Body); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + handshake.NotifyHelloComplete(); + + ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.maxFragmentLength); + + if (state.resumedSession) + { + securityParameters.masterSecret = Arrays.Clone(state.sessionParameters.MasterSecret); + recordLayer.InitPendingEpoch(state.client.GetCipher()); + + // NOTE: Calculated exclusive of the actual Finished message from the server + byte[] resExpectedServerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); + ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), resExpectedServerVerifyData); + + // NOTE: Calculated exclusive of the Finished message itself + byte[] resClientVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.client_finished, + TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); + handshake.SendMessage(HandshakeType.finished, resClientVerifyData); + + handshake.Finish(); + + state.clientContext.SetResumableSession(state.tlsSession); + + state.client.NotifyHandshakeComplete(); + + return new DtlsTransport(recordLayer); + } + + InvalidateSession(state); + + if (state.selectedSessionID.Length > 0) + { + state.tlsSession = new TlsSessionImpl(state.selectedSessionID, null); + } + + serverMessage = handshake.ReceiveMessage(); + + if (serverMessage.Type == HandshakeType.supplemental_data) + { + ProcessServerSupplementalData(state, serverMessage.Body); + serverMessage = handshake.ReceiveMessage(); + } + else + { + state.client.ProcessServerSupplementalData(null); + } + + state.keyExchange = state.client.GetKeyExchange(); + state.keyExchange.Init(state.clientContext); + + Certificate serverCertificate = null; + + if (serverMessage.Type == HandshakeType.certificate) + { + serverCertificate = ProcessServerCertificate(state, serverMessage.Body); + serverMessage = handshake.ReceiveMessage(); + } + else + { + // Okay, Certificate is optional + state.keyExchange.SkipServerCredentials(); + } + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.IsEmpty) + { + state.allowCertificateStatus = false; + } + + if (serverMessage.Type == HandshakeType.certificate_status) + { + ProcessCertificateStatus(state, serverMessage.Body); + serverMessage = handshake.ReceiveMessage(); + } + else + { + // Okay, CertificateStatus is optional + } + + if (serverMessage.Type == HandshakeType.server_key_exchange) + { + ProcessServerKeyExchange(state, serverMessage.Body); + serverMessage = handshake.ReceiveMessage(); + } + else + { + // Okay, ServerKeyExchange is optional + state.keyExchange.SkipServerKeyExchange(); + } + + if (serverMessage.Type == HandshakeType.certificate_request) + { + ProcessCertificateRequest(state, serverMessage.Body); + + /* + * TODO Give the client a chance to immediately select the CertificateVerify hash + * algorithm here to avoid tracking the other hash algorithms unnecessarily? + */ + TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, + state.certificateRequest.SupportedSignatureAlgorithms); + + serverMessage = handshake.ReceiveMessage(); + } + else + { + // Okay, CertificateRequest is optional + } + + if (serverMessage.Type == HandshakeType.server_hello_done) + { + if (serverMessage.Body.Length != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + handshake.HandshakeHash.SealHashAlgorithms(); + + IList clientSupplementalData = state.client.GetClientSupplementalData(); + if (clientSupplementalData != null) + { + byte[] supplementalDataBody = GenerateSupplementalData(clientSupplementalData); + handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody); + } + + if (state.certificateRequest != null) + { + state.clientCredentials = state.authentication.GetClientCredentials(state.certificateRequest); + + /* + * RFC 5246 If no suitable certificate is available, the client MUST send a certificate + * message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + Certificate clientCertificate = null; + if (state.clientCredentials != null) + { + clientCertificate = state.clientCredentials.Certificate; + } + if (clientCertificate == null) + { + clientCertificate = Certificate.EmptyChain; + } + + byte[] certificateBody = GenerateCertificate(clientCertificate); + handshake.SendMessage(HandshakeType.certificate, certificateBody); + } + + if (state.clientCredentials != null) + { + state.keyExchange.ProcessClientCredentials(state.clientCredentials); + } + else + { + state.keyExchange.SkipClientCredentials(); + } + + byte[] clientKeyExchangeBody = GenerateClientKeyExchange(state); + handshake.SendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody); + + TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish(); + securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.clientContext, prepareFinishHash, null); + + TlsProtocol.EstablishMasterSecret(state.clientContext, state.keyExchange); + recordLayer.InitPendingEpoch(state.client.GetCipher()); + + if (state.clientCredentials != null && state.clientCredentials is TlsSignerCredentials) + { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials; + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm( + state.clientContext, signerCredentials); + + byte[] hash; + if (signatureAndHashAlgorithm == null) + { + hash = securityParameters.SessionHash; + } + else + { + hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash); + } + + byte[] signature = signerCredentials.GenerateCertificateSignature(hash); + DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature); + byte[] certificateVerifyBody = GenerateCertificateVerify(state, certificateVerify); + handshake.SendMessage(HandshakeType.certificate_verify, certificateVerifyBody); + } + + // NOTE: Calculated exclusive of the Finished message itself + byte[] clientVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.client_finished, + TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); + handshake.SendMessage(HandshakeType.finished, clientVerifyData); + + if (state.expectSessionTicket) + { + serverMessage = handshake.ReceiveMessage(); + if (serverMessage.Type == HandshakeType.session_ticket) + { + ProcessNewSessionTicket(state, serverMessage.Body); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + // NOTE: Calculated exclusive of the actual Finished message from the server + byte[] expectedServerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); + ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedServerVerifyData); + + handshake.Finish(); + + if (state.tlsSession != null) + { + state.sessionParameters = new SessionParameters.Builder() + .SetCipherSuite(securityParameters.CipherSuite) + .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm) + .SetMasterSecret(securityParameters.MasterSecret) + .SetPeerCertificate(serverCertificate) + .SetPskIdentity(securityParameters.PskIdentity) + .SetSrpIdentity(securityParameters.SrpIdentity) + // TODO Consider filtering extensions that aren't relevant to resumed sessions + .SetServerExtensions(state.serverExtensions) + .Build(); + + state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters); + + state.clientContext.SetResumableSession(state.tlsSession); + } + + state.client.NotifyHandshakeComplete(); + + return new DtlsTransport(recordLayer); + } + + protected virtual byte[] GenerateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify) + { + MemoryStream buf = new MemoryStream(); + certificateVerify.Encode(buf); + return buf.ToArray(); + } + + protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client) + { + MemoryStream buf = new MemoryStream(); + + ProtocolVersion client_version = client.ClientVersion; + if (!client_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.internal_error); + + TlsClientContextImpl context = state.clientContext; + + context.SetClientVersion(client_version); + TlsUtilities.WriteVersion(client_version, buf); + + SecurityParameters securityParameters = context.SecurityParameters; + buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length); + + // Session ID + byte[] session_id = TlsUtilities.EmptyBytes; + if (state.tlsSession != null) + { + session_id = state.tlsSession.SessionID; + if (session_id == null || session_id.Length > 32) + { + session_id = TlsUtilities.EmptyBytes; + } + } + TlsUtilities.WriteOpaque8(session_id, buf); + + // Cookie + TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); + + bool fallback = client.IsFallback; + + /* + * Cipher suites + */ + state.offeredCipherSuites = client.GetCipherSuites(); + + // Integer -> byte[] + state.clientExtensions = client.GetClientExtensions(); + + // Cipher Suites (and SCSV) + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info); + bool noRenegExt = (null == renegExtData); + + bool noRenegSCSV = !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if (noRenegExt && noRenegSCSV) + { + // TODO Consider whether to default to a client extension instead + state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + /* + * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version + * containing a lower value than the latest (highest-valued) version supported by the + * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in + * ClientHello.cipher_suites. + */ + if (fallback && !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)) + { + state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV); + } + + TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf); + } + + // TODO Add support for compression + // Compression methods + // state.offeredCompressionMethods = client.getCompressionMethods(); + state.offeredCompressionMethods = new byte[]{ CompressionMethod.cls_null }; + + TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf); + + // Extensions + if (state.clientExtensions != null) + { + TlsProtocol.WriteExtensions(buf, state.clientExtensions); + } + + return buf.ToArray(); + } + + protected virtual byte[] GenerateClientKeyExchange(ClientHandshakeState state) + { + MemoryStream buf = new MemoryStream(); + state.keyExchange.GenerateClientKeyExchange(buf); + return buf.ToArray(); + } + + protected virtual void InvalidateSession(ClientHandshakeState state) + { + if (state.sessionParameters != null) + { + state.sessionParameters.Clear(); + state.sessionParameters = null; + } + + if (state.tlsSession != null) + { + state.tlsSession.Invalidate(); + state.tlsSession = null; + } + } + + protected virtual void ProcessCertificateRequest(ClientHandshakeState state, byte[] body) + { + if (state.authentication == null) + { + /* + * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server to + * request client identification. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + MemoryStream buf = new MemoryStream(body, false); + + state.certificateRequest = CertificateRequest.Parse(state.clientContext, buf); + + TlsProtocol.AssertEmpty(buf); + + state.keyExchange.ValidateCertificateRequest(state.certificateRequest); + } + + protected virtual void ProcessCertificateStatus(ClientHandshakeState state, byte[] body) + { + if (!state.allowCertificateStatus) + { + /* + * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the + * server MUST have included an extension of type "status_request" with empty + * "extension_data" in the extended server hello.. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + MemoryStream buf = new MemoryStream(body, false); + + state.certificateStatus = CertificateStatus.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + // TODO[RFC 3546] Figure out how to provide this to the client/authentication. + } + + protected virtual byte[] ProcessHelloVerifyRequest(ClientHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); + byte[] cookie = TlsUtilities.ReadOpaque8(buf); + + TlsProtocol.AssertEmpty(buf); + + // TODO Seems this behaviour is not yet in line with OpenSSL for DTLS 1.2 + // reportServerVersion(state, server_version); + if (!server_version.IsEqualOrEarlierVersionOf(state.clientContext.ClientVersion)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + /* + * RFC 6347 This specification increases the cookie size limit to 255 bytes for greater + * future flexibility. The limit remains 32 for previous versions of DTLS. + */ + if (!ProtocolVersion.DTLSv12.IsEqualOrEarlierVersionOf(server_version) && cookie.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return cookie; + } + + protected virtual void ProcessNewSessionTicket(ClientHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + state.client.NotifyNewSessionTicket(newSessionTicket); + } + + protected virtual Certificate ProcessServerCertificate(ClientHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + Certificate serverCertificate = Certificate.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + state.keyExchange.ProcessServerCertificate(serverCertificate); + state.authentication = state.client.GetAuthentication(); + state.authentication.NotifyServerCertificate(serverCertificate); + + return serverCertificate; + } + + protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] body) + { + SecurityParameters securityParameters = state.clientContext.SecurityParameters; + + MemoryStream buf = new MemoryStream(body, false); + + { + ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); + ReportServerVersion(state, server_version); + } + + securityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); + + state.selectedSessionID = TlsUtilities.ReadOpaque8(buf); + if (state.selectedSessionID.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + state.client.NotifySessionID(state.selectedSessionID); + state.resumedSession = state.selectedSessionID.Length > 0 && state.tlsSession != null + && Arrays.AreEqual(state.selectedSessionID, state.tlsSession.SessionID); + + int selectedCipherSuite = TlsUtilities.ReadUint16(buf); + if (!Arrays.Contains(state.offeredCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || CipherSuite.IsScsv(selectedCipherSuite) + || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, state.clientContext.ServerVersion)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + ValidateSelectedCipherSuite(selectedCipherSuite, AlertDescription.illegal_parameter); + state.client.NotifySelectedCipherSuite(selectedCipherSuite); + + byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); + if (!Arrays.Contains(state.offeredCompressionMethods, selectedCompressionMethod)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + state.client.NotifySelectedCompressionMethod(selectedCompressionMethod); + + /* + * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server + * hello message when the client has requested extended functionality via the extended + * client hello message specified in Section 2.1. ... Note that the extended server hello + * message is only sent in response to an extended client hello message. This prevents the + * possibility that the extended server hello message could "break" existing TLS 1.0 + * clients. + */ + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + + // Integer -> byte[] + state.serverExtensions = TlsProtocol.ReadExtensions(buf); + + /* + * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an + * extended client hello message. However, see RFC 5746 exception below. We always include + * the SCSV, so an Extended Server Hello is always allowed. + */ + if (state.serverExtensions != null) + { + foreach (int extType in state.serverExtensions.Keys) + { + /* + * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a + * ClientHello containing only the SCSV is an explicit exception to the prohibition + * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is + * only allowed because the client is signaling its willingness to receive the + * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + if (extType == ExtensionType.renegotiation_info) + continue; + + /* + * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the + * same extension type appeared in the corresponding ClientHello. If a client + * receives an extension type in ServerHello that it did not request in the + * associated ClientHello, it MUST abort the handshake with an unsupported_extension + * fatal alert. + */ + if (null == TlsUtilities.GetExtensionData(state.clientExtensions, extType)) + throw new TlsFatalAlert(AlertDescription.unsupported_extension); + + /* + * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions[.] + */ + if (state.resumedSession) + { + // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats + // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats + // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats + //throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + /* + * RFC 5746 3.4. Client Behavior: Initial Handshake + */ + { + /* + * When a ServerHello is received, the client MUST check if it includes the + * "renegotiation_info" extension: + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions, ExtensionType.renegotiation_info); + if (renegExtData != null) + { + /* + * If the extension is present, set the secure_renegotiation flag to TRUE. The + * client MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake (by sending a fatal + * handshake_failure alert). + */ + state.secure_renegotiation = true; + + if (!Arrays.ConstantTimeAreEqual(renegExtData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming + state.client.NotifySecureRenegotiation(state.secure_renegotiation); + + IDictionary sessionClientExtensions = state.clientExtensions, sessionServerExtensions = state.serverExtensions; + if (state.resumedSession) + { + if (selectedCipherSuite != state.sessionParameters.CipherSuite + || selectedCompressionMethod != state.sessionParameters.CompressionAlgorithm) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + sessionClientExtensions = null; + sessionServerExtensions = state.sessionParameters.ReadServerExtensions(); + } + + securityParameters.cipherSuite = selectedCipherSuite; + securityParameters.compressionAlgorithm = selectedCompressionMethod; + + if (sessionServerExtensions != null) + { + { + /* + * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client + * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) + * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the + * client. + */ + bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions); + if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(securityParameters.CipherSuite)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + securityParameters.encryptThenMac = serverSentEncryptThenMAC; + } + + securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); + + securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession, + sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); + + securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be + * sent in a session resumption handshake. + */ + state.allowCertificateStatus = !state.resumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request, + AlertDescription.illegal_parameter); + + state.expectSessionTicket = !state.resumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket, + AlertDescription.illegal_parameter); + } + + /* + * TODO[session-hash] + * + * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes + * that do not use the extended master secret [..]. (and see 5.2, 5.3) + */ + + if (sessionClientExtensions != null) + { + state.client.ProcessServerExtensions(sessionServerExtensions); + } + + securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.clientContext, + securityParameters.CipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has + * a verify_data_length equal to 12. This includes all existing cipher suites. + */ + securityParameters.verifyDataLength = 12; + } + + protected virtual void ProcessServerKeyExchange(ClientHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + state.keyExchange.ProcessServerKeyExchange(buf); + + TlsProtocol.AssertEmpty(buf); + } + + protected virtual void ProcessServerSupplementalData(ClientHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + IList serverSupplementalData = TlsProtocol.ReadSupplementalDataMessage(buf); + state.client.ProcessServerSupplementalData(serverSupplementalData); + } + + protected virtual void ReportServerVersion(ClientHandshakeState state, ProtocolVersion server_version) + { + TlsClientContextImpl clientContext = state.clientContext; + ProtocolVersion currentServerVersion = clientContext.ServerVersion; + if (null == currentServerVersion) + { + clientContext.SetServerVersion(server_version); + state.client.NotifyServerVersion(server_version); + } + else if (!currentServerVersion.Equals(server_version)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + protected static byte[] PatchClientHelloWithCookie(byte[] clientHelloBody, byte[] cookie) + { + int sessionIDPos = 34; + int sessionIDLength = TlsUtilities.ReadUint8(clientHelloBody, sessionIDPos); + + int cookieLengthPos = sessionIDPos + 1 + sessionIDLength; + int cookiePos = cookieLengthPos + 1; + + byte[] patched = new byte[clientHelloBody.Length + cookie.Length]; + Array.Copy(clientHelloBody, 0, patched, 0, cookieLengthPos); + TlsUtilities.CheckUint8(cookie.Length); + TlsUtilities.WriteUint8((byte)cookie.Length, patched, cookieLengthPos); + Array.Copy(cookie, 0, patched, cookiePos, cookie.Length); + Array.Copy(clientHelloBody, cookiePos, patched, cookiePos + cookie.Length, clientHelloBody.Length - cookiePos); + + return patched; + } + + protected internal class ClientHandshakeState + { + internal TlsClient client = null; + internal TlsClientContextImpl clientContext = null; + internal TlsSession tlsSession = null; + internal SessionParameters sessionParameters = null; + internal SessionParameters.Builder sessionParametersBuilder = null; + internal int[] offeredCipherSuites = null; + internal byte[] offeredCompressionMethods = null; + internal IDictionary clientExtensions = null; + internal IDictionary serverExtensions = null; + internal byte[] selectedSessionID = null; + internal bool resumedSession = false; + internal bool secure_renegotiation = false; + internal bool allowCertificateStatus = false; + internal bool expectSessionTicket = false; + internal TlsKeyExchange keyExchange = null; + internal TlsAuthentication authentication = null; + internal CertificateStatus certificateStatus = null; + internal CertificateRequest certificateRequest = null; + internal TlsCredentials clientCredentials = null; + } + } +} diff --git a/crypto/src/crypto/tls/DtlsEpoch.cs b/crypto/src/crypto/tls/DtlsEpoch.cs new file mode 100644
index 000000000..91fffa5e1 --- /dev/null +++ b/crypto/src/crypto/tls/DtlsEpoch.cs
@@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class DtlsEpoch + { + private readonly DtlsReplayWindow mReplayWindow = new DtlsReplayWindow(); + + private readonly int mEpoch; + private readonly TlsCipher mCipher; + + private long mSequenceNumber = 0; + + internal DtlsEpoch(int epoch, TlsCipher cipher) + { + if (epoch < 0) + throw new ArgumentException("must be >= 0", "epoch"); + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.mEpoch = epoch; + this.mCipher = cipher; + } + + internal long AllocateSequenceNumber() + { + // TODO Check for overflow + return mSequenceNumber++; + } + + internal TlsCipher Cipher + { + get { return mCipher; } + } + + internal int Epoch + { + get { return mEpoch; } + } + + internal DtlsReplayWindow ReplayWindow + { + get { return mReplayWindow; } + } + + internal long SequenceNumber + { + get { return mSequenceNumber; } + } + } +} diff --git a/crypto/src/crypto/tls/DtlsHandshakeRetransmit.cs b/crypto/src/crypto/tls/DtlsHandshakeRetransmit.cs new file mode 100644
index 000000000..8bfae78b1 --- /dev/null +++ b/crypto/src/crypto/tls/DtlsHandshakeRetransmit.cs
@@ -0,0 +1,11 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + interface DtlsHandshakeRetransmit + { + /// <exception cref="IOException"/> + void ReceivedHandshakeRecord(int epoch, byte[] buf, int off, int len); + } +} diff --git a/crypto/src/crypto/tls/DtlsProtocol.cs b/crypto/src/crypto/tls/DtlsProtocol.cs new file mode 100644
index 000000000..e4ebd436c --- /dev/null +++ b/crypto/src/crypto/tls/DtlsProtocol.cs
@@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class DtlsProtocol + { + protected readonly SecureRandom mSecureRandom; + + protected DtlsProtocol(SecureRandom secureRandom) + { + if (secureRandom == null) + throw new ArgumentNullException("secureRandom"); + + this.mSecureRandom = secureRandom; + } + + /// <exception cref="IOException"/> + protected virtual void ProcessFinished(byte[] body, byte[] expected_verify_data) + { + MemoryStream buf = new MemoryStream(body, false); + + byte[] verify_data = TlsUtilities.ReadFully(expected_verify_data.Length, buf); + + TlsProtocol.AssertEmpty(buf); + + if (!Arrays.ConstantTimeAreEqual(expected_verify_data, verify_data)) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + /// <exception cref="IOException"/> + internal static void ApplyMaxFragmentLengthExtension(DtlsRecordLayer recordLayer, short maxFragmentLength) + { + if (maxFragmentLength >= 0) + { + if (!MaxFragmentLength.IsValid((byte)maxFragmentLength)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + int plainTextLimit = 1 << (8 + maxFragmentLength); + recordLayer.SetPlaintextLimit(plainTextLimit); + } + } + + /// <exception cref="IOException"/> + protected static short EvaluateMaxFragmentLengthExtension(bool resumedSession, IDictionary clientExtensions, + IDictionary serverExtensions, byte alertDescription) + { + short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions); + if (maxFragmentLength >= 0) + { + if (!MaxFragmentLength.IsValid((byte)maxFragmentLength) + || (!resumedSession && maxFragmentLength != TlsExtensionsUtilities + .GetMaxFragmentLengthExtension(clientExtensions))) + { + throw new TlsFatalAlert(alertDescription); + } + } + return maxFragmentLength; + } + + /// <exception cref="IOException"/> + protected static byte[] GenerateCertificate(Certificate certificate) + { + MemoryStream buf = new MemoryStream(); + certificate.Encode(buf); + return buf.ToArray(); + } + + /// <exception cref="IOException"/> + protected static byte[] GenerateSupplementalData(IList supplementalData) + { + MemoryStream buf = new MemoryStream(); + TlsProtocol.WriteSupplementalData(buf, supplementalData); + return buf.ToArray(); + } + + /// <exception cref="IOException"/> + protected static void ValidateSelectedCipherSuite(int selectedCipherSuite, byte alertDescription) + { + switch (TlsUtilities.GetEncryptionAlgorithm(selectedCipherSuite)) + { + case EncryptionAlgorithm.RC4_40: + case EncryptionAlgorithm.RC4_128: + throw new TlsFatalAlert(alertDescription); + } + } + } +} diff --git a/crypto/src/crypto/tls/DtlsReassembler.cs b/crypto/src/crypto/tls/DtlsReassembler.cs new file mode 100644
index 000000000..11fe609cf --- /dev/null +++ b/crypto/src/crypto/tls/DtlsReassembler.cs
@@ -0,0 +1,125 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + class DtlsReassembler + { + private readonly byte mMsgType; + private readonly byte[] mBody; + + private readonly IList mMissing = Platform.CreateArrayList(); + + internal DtlsReassembler(byte msg_type, int length) + { + this.mMsgType = msg_type; + this.mBody = new byte[length]; + this.mMissing.Add(new Range(0, length)); + } + + internal byte MsgType + { + get { return mMsgType; } + } + + internal byte[] GetBodyIfComplete() + { + return mMissing.Count == 0 ? mBody : null; + } + + internal void ContributeFragment(byte msg_type, int length, byte[] buf, int off, int fragment_offset, + int fragment_length) + { + int fragment_end = fragment_offset + fragment_length; + + if (this.mMsgType != msg_type || this.mBody.Length != length || fragment_end > length) + { + return; + } + + if (fragment_length == 0) + { + // NOTE: Empty messages still require an empty fragment to complete it + if (fragment_offset == 0 && mMissing.Count > 0) + { + Range firstRange = (Range)mMissing[0]; + if (firstRange.End == 0) + { + mMissing.RemoveAt(0); + } + } + return; + } + + for (int i = 0; i < mMissing.Count; ++i) + { + Range range = (Range)mMissing[i]; + if (range.Start >= fragment_end) + { + break; + } + if (range.End > fragment_offset) + { + + int copyStart = System.Math.Max(range.Start, fragment_offset); + int copyEnd = System.Math.Min(range.End, fragment_end); + int copyLength = copyEnd - copyStart; + + Array.Copy(buf, off + copyStart - fragment_offset, mBody, copyStart, + copyLength); + + if (copyStart == range.Start) + { + if (copyEnd == range.End) + { + mMissing.RemoveAt(i--); + } + else + { + range.Start = copyEnd; + } + } + else + { + if (copyEnd != range.End) + { + mMissing.Insert(++i, new Range(copyEnd, range.End)); + } + range.End = copyStart; + } + } + } + } + + internal void Reset() + { + this.mMissing.Clear(); + this.mMissing.Add(new Range(0, mBody.Length)); + } + + private class Range + { + private int mStart, mEnd; + + internal Range(int start, int end) + { + this.mStart = start; + this.mEnd = end; + } + + public int Start + { + get { return mStart; } + set { this.mStart = value; } + } + + public int End + { + get { return mEnd; } + set { this.mEnd = value; } + } + } + } +} diff --git a/crypto/src/crypto/tls/DtlsRecordLayer.cs b/crypto/src/crypto/tls/DtlsRecordLayer.cs new file mode 100644
index 000000000..70befd9e4 --- /dev/null +++ b/crypto/src/crypto/tls/DtlsRecordLayer.cs
@@ -0,0 +1,507 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class DtlsRecordLayer + : DatagramTransport + { + private const int RECORD_HEADER_LENGTH = 13; + private const int MAX_FRAGMENT_LENGTH = 1 << 14; + private const long TCP_MSL = 1000L * 60 * 2; + private const long RETRANSMIT_TIMEOUT = TCP_MSL * 2; + + private readonly DatagramTransport mTransport; + private readonly TlsContext mContext; + private readonly TlsPeer mPeer; + + private readonly ByteQueue mRecordQueue = new ByteQueue(); + + private volatile bool mClosed = false; + private volatile bool mFailed = false; + private volatile ProtocolVersion mDiscoveredPeerVersion = null; + private volatile bool mInHandshake; + private volatile int mPlaintextLimit; + private DtlsEpoch mCurrentEpoch, mPendingEpoch; + private DtlsEpoch mReadEpoch, mWriteEpoch; + + private DtlsHandshakeRetransmit mRetransmit = null; + private DtlsEpoch mRetransmitEpoch = null; + private long mRetransmitExpiry = 0; + + internal DtlsRecordLayer(DatagramTransport transport, TlsContext context, TlsPeer peer, byte contentType) + { + this.mTransport = transport; + this.mContext = context; + this.mPeer = peer; + + this.mInHandshake = true; + + this.mCurrentEpoch = new DtlsEpoch(0, new TlsNullCipher(context)); + this.mPendingEpoch = null; + this.mReadEpoch = mCurrentEpoch; + this.mWriteEpoch = mCurrentEpoch; + + SetPlaintextLimit(MAX_FRAGMENT_LENGTH); + } + + internal virtual void SetPlaintextLimit(int plaintextLimit) + { + this.mPlaintextLimit = plaintextLimit; + } + + internal virtual ProtocolVersion DiscoveredPeerVersion + { + get { return mDiscoveredPeerVersion; } + } + + internal virtual ProtocolVersion ResetDiscoveredPeerVersion() + { + ProtocolVersion result = mDiscoveredPeerVersion; + mDiscoveredPeerVersion = null; + return result; + } + + internal virtual void InitPendingEpoch(TlsCipher pendingCipher) + { + if (mPendingEpoch != null) + throw new InvalidOperationException(); + + /* + * TODO "In order to ensure that any given sequence/epoch pair is unique, implementations + * MUST NOT allow the same epoch value to be reused within two times the TCP maximum segment + * lifetime." + */ + + // TODO Check for overflow + this.mPendingEpoch = new DtlsEpoch(mWriteEpoch.Epoch + 1, pendingCipher); + } + + internal virtual void HandshakeSuccessful(DtlsHandshakeRetransmit retransmit) + { + if (mReadEpoch == mCurrentEpoch || mWriteEpoch == mCurrentEpoch) + { + // TODO + throw new InvalidOperationException(); + } + + if (retransmit != null) + { + this.mRetransmit = retransmit; + this.mRetransmitEpoch = mCurrentEpoch; + this.mRetransmitExpiry = DateTimeUtilities.CurrentUnixMs() + RETRANSMIT_TIMEOUT; + } + + this.mInHandshake = false; + this.mCurrentEpoch = mPendingEpoch; + this.mPendingEpoch = null; + } + + internal virtual void ResetWriteEpoch() + { + if (mRetransmitEpoch != null) + { + this.mWriteEpoch = mRetransmitEpoch; + } + else + { + this.mWriteEpoch = mCurrentEpoch; + } + } + + public virtual int GetReceiveLimit() + { + return System.Math.Min(this.mPlaintextLimit, + mReadEpoch.Cipher.GetPlaintextLimit(mTransport.GetReceiveLimit() - RECORD_HEADER_LENGTH)); + } + + public virtual int GetSendLimit() + { + return System.Math.Min(this.mPlaintextLimit, + mWriteEpoch.Cipher.GetPlaintextLimit(mTransport.GetSendLimit() - RECORD_HEADER_LENGTH)); + } + + public virtual int Receive(byte[] buf, int off, int len, int waitMillis) + { + byte[] record = null; + + for (;;) + { + int receiveLimit = System.Math.Min(len, GetReceiveLimit()) + RECORD_HEADER_LENGTH; + if (record == null || record.Length < receiveLimit) + { + record = new byte[receiveLimit]; + } + + try + { + if (mRetransmit != null && DateTimeUtilities.CurrentUnixMs() > mRetransmitExpiry) + { + mRetransmit = null; + mRetransmitEpoch = null; + } + + int received = ReceiveRecord(record, 0, receiveLimit, waitMillis); + if (received < 0) + { + return received; + } + if (received < RECORD_HEADER_LENGTH) + { + continue; + } + int length = TlsUtilities.ReadUint16(record, 11); + if (received != (length + RECORD_HEADER_LENGTH)) + { + continue; + } + + byte type = TlsUtilities.ReadUint8(record, 0); + + // TODO Support user-specified custom protocols? + switch (type) + { + case ContentType.alert: + case ContentType.application_data: + case ContentType.change_cipher_spec: + case ContentType.handshake: + case ContentType.heartbeat: + break; + default: + // TODO Exception? + continue; + } + + int epoch = TlsUtilities.ReadUint16(record, 3); + + DtlsEpoch recordEpoch = null; + if (epoch == mReadEpoch.Epoch) + { + recordEpoch = mReadEpoch; + } + else if (type == ContentType.handshake && mRetransmitEpoch != null + && epoch == mRetransmitEpoch.Epoch) + { + recordEpoch = mRetransmitEpoch; + } + + if (recordEpoch == null) + { + continue; + } + + long seq = TlsUtilities.ReadUint48(record, 5); + if (recordEpoch.ReplayWindow.ShouldDiscard(seq)) + { + continue; + } + + ProtocolVersion version = TlsUtilities.ReadVersion(record, 1); + if (mDiscoveredPeerVersion != null && !mDiscoveredPeerVersion.Equals(version)) + { + continue; + } + + byte[] plaintext = recordEpoch.Cipher.DecodeCiphertext( + GetMacSequenceNumber(recordEpoch.Epoch, seq), type, record, RECORD_HEADER_LENGTH, + received - RECORD_HEADER_LENGTH); + + recordEpoch.ReplayWindow.ReportAuthenticated(seq); + + if (plaintext.Length > this.mPlaintextLimit) + { + continue; + } + + if (mDiscoveredPeerVersion == null) + { + mDiscoveredPeerVersion = version; + } + + switch (type) + { + case ContentType.alert: + { + if (plaintext.Length == 2) + { + byte alertLevel = plaintext[0]; + byte alertDescription = plaintext[1]; + + mPeer.NotifyAlertReceived(alertLevel, alertDescription); + + if (alertLevel == AlertLevel.fatal) + { + Fail(alertDescription); + throw new TlsFatalAlert(alertDescription); + } + + // TODO Can close_notify be a fatal alert? + if (alertDescription == AlertDescription.close_notify) + { + CloseTransport(); + } + } + + continue; + } + case ContentType.application_data: + { + if (mInHandshake) + { + // TODO Consider buffering application data for new epoch that arrives + // out-of-order with the Finished message + continue; + } + break; + } + case ContentType.change_cipher_spec: + { + // Implicitly receive change_cipher_spec and change to pending cipher state + + for (int i = 0; i < plaintext.Length; ++i) + { + byte message = TlsUtilities.ReadUint8(plaintext, i); + if (message != ChangeCipherSpec.change_cipher_spec) + { + continue; + } + + if (mPendingEpoch != null) + { + mReadEpoch = mPendingEpoch; + } + } + + continue; + } + case ContentType.handshake: + { + if (!mInHandshake) + { + if (mRetransmit != null) + { + mRetransmit.ReceivedHandshakeRecord(epoch, plaintext, 0, plaintext.Length); + } + + // TODO Consider support for HelloRequest + continue; + } + break; + } + case ContentType.heartbeat: + { + // TODO[RFC 6520] + continue; + } + } + + /* + * NOTE: If we receive any non-handshake data in the new epoch implies the peer has + * received our final flight. + */ + if (!mInHandshake && mRetransmit != null) + { + this.mRetransmit = null; + this.mRetransmitEpoch = null; + } + + Array.Copy(plaintext, 0, buf, off, plaintext.Length); + return plaintext.Length; + } + catch (IOException e) + { + // NOTE: Assume this is a timeout for the moment + throw e; + } + } + } + + /// <exception cref="IOException"/> + public virtual void Send(byte[] buf, int off, int len) + { + byte contentType = ContentType.application_data; + + if (this.mInHandshake || this.mWriteEpoch == this.mRetransmitEpoch) + { + contentType = ContentType.handshake; + + byte handshakeType = TlsUtilities.ReadUint8(buf, off); + if (handshakeType == HandshakeType.finished) + { + DtlsEpoch nextEpoch = null; + if (this.mInHandshake) + { + nextEpoch = mPendingEpoch; + } + else if (this.mWriteEpoch == this.mRetransmitEpoch) + { + nextEpoch = mCurrentEpoch; + } + + if (nextEpoch == null) + { + // TODO + throw new InvalidOperationException(); + } + + // Implicitly send change_cipher_spec and change to pending cipher state + + // TODO Send change_cipher_spec and finished records in single datagram? + byte[] data = new byte[]{ 1 }; + SendRecord(ContentType.change_cipher_spec, data, 0, data.Length); + + mWriteEpoch = nextEpoch; + } + } + + SendRecord(contentType, buf, off, len); + } + + public virtual void Close() + { + if (!mClosed) + { + if (mInHandshake) + { + Warn(AlertDescription.user_canceled, "User canceled handshake"); + } + CloseTransport(); + } + } + + internal virtual void Fail(byte alertDescription) + { + if (!mClosed) + { + try + { + RaiseAlert(AlertLevel.fatal, alertDescription, null, null); + } + catch (Exception) + { + // Ignore + } + + mFailed = true; + + CloseTransport(); + } + } + + internal virtual void Warn(byte alertDescription, string message) + { + RaiseAlert(AlertLevel.warning, alertDescription, message, null); + } + + private void CloseTransport() + { + if (!mClosed) + { + /* + * RFC 5246 7.2.1. Unless some other fatal alert has been transmitted, each party is + * required to send a close_notify alert before closing the write side of the + * connection. The other party MUST respond with a close_notify alert of its own and + * close down the connection immediately, discarding any pending writes. + */ + + try + { + if (!mFailed) + { + Warn(AlertDescription.close_notify, null); + } + mTransport.Close(); + } + catch (Exception) + { + // Ignore + } + + mClosed = true; + } + } + + private void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause) + { + mPeer.NotifyAlertRaised(alertLevel, alertDescription, message, cause); + + byte[] error = new byte[2]; + error[0] = (byte)alertLevel; + error[1] = (byte)alertDescription; + + SendRecord(ContentType.alert, error, 0, 2); + } + + private int ReceiveRecord(byte[] buf, int off, int len, int waitMillis) + { + if (mRecordQueue.Available > 0) + { + int length = 0; + if (mRecordQueue.Available >= RECORD_HEADER_LENGTH) + { + byte[] lengthBytes = new byte[2]; + mRecordQueue.Read(lengthBytes, 0, 2, 11); + length = TlsUtilities.ReadUint16(lengthBytes, 0); + } + + int received = System.Math.Min(mRecordQueue.Available, RECORD_HEADER_LENGTH + length); + mRecordQueue.RemoveData(buf, off, received, 0); + return received; + } + + { + int received = mTransport.Receive(buf, off, len, waitMillis); + if (received >= RECORD_HEADER_LENGTH) + { + int fragmentLength = TlsUtilities.ReadUint16(buf, off + 11); + int recordLength = RECORD_HEADER_LENGTH + fragmentLength; + if (received > recordLength) + { + mRecordQueue.AddData(buf, off + recordLength, received - recordLength); + received = recordLength; + } + } + return received; + } + } + + private void SendRecord(byte contentType, byte[] buf, int off, int len) + { + if (len > this.mPlaintextLimit) + throw new TlsFatalAlert(AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (len < 1 && contentType != ContentType.application_data) + throw new TlsFatalAlert(AlertDescription.internal_error); + + int recordEpoch = mWriteEpoch.Epoch; + long recordSequenceNumber = mWriteEpoch.AllocateSequenceNumber(); + + byte[] ciphertext = mWriteEpoch.Cipher.EncodePlaintext( + GetMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len); + + // TODO Check the ciphertext length? + + byte[] record = new byte[ciphertext.Length + RECORD_HEADER_LENGTH]; + TlsUtilities.WriteUint8(contentType, record, 0); + ProtocolVersion version = mDiscoveredPeerVersion != null ? mDiscoveredPeerVersion : mContext.ClientVersion; + TlsUtilities.WriteVersion(version, record, 1); + TlsUtilities.WriteUint16(recordEpoch, record, 3); + TlsUtilities.WriteUint48(recordSequenceNumber, record, 5); + TlsUtilities.WriteUint16(ciphertext.Length, record, 11); + Array.Copy(ciphertext, 0, record, RECORD_HEADER_LENGTH, ciphertext.Length); + + mTransport.Send(record, 0, record.Length); + } + + private static long GetMacSequenceNumber(int epoch, long sequence_number) + { + return ((epoch & 0xFFFFFFFFL) << 48) | sequence_number; + } + } +} diff --git a/crypto/src/crypto/tls/DtlsReliableHandshake.cs b/crypto/src/crypto/tls/DtlsReliableHandshake.cs new file mode 100644
index 000000000..8e4439e67 --- /dev/null +++ b/crypto/src/crypto/tls/DtlsReliableHandshake.cs
@@ -0,0 +1,443 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class DtlsReliableHandshake + { + private const int MAX_RECEIVE_AHEAD = 10; + + private readonly DtlsRecordLayer mRecordLayer; + + private TlsHandshakeHash mHandshakeHash; + + private IDictionary mCurrentInboundFlight = Platform.CreateHashtable(); + private IDictionary mPreviousInboundFlight = null; + private IList mOutboundFlight = Platform.CreateArrayList(); + private bool mSending = true; + + private int mMessageSeq = 0, mNextReceiveSeq = 0; + + internal DtlsReliableHandshake(TlsContext context, DtlsRecordLayer transport) + { + this.mRecordLayer = transport; + this.mHandshakeHash = new DeferredHash(); + this.mHandshakeHash.Init(context); + } + + internal void NotifyHelloComplete() + { + this.mHandshakeHash = mHandshakeHash.NotifyPrfDetermined(); + } + + internal TlsHandshakeHash HandshakeHash + { + get { return mHandshakeHash; } + } + + internal TlsHandshakeHash PrepareToFinish() + { + TlsHandshakeHash result = mHandshakeHash; + this.mHandshakeHash = mHandshakeHash.StopTracking(); + return result; + } + + internal void SendMessage(byte msg_type, byte[] body) + { + TlsUtilities.CheckUint24(body.Length); + + if (!mSending) + { + CheckInboundFlight(); + mSending = true; + mOutboundFlight.Clear(); + } + + Message message = new Message(mMessageSeq++, msg_type, body); + + mOutboundFlight.Add(message); + + WriteMessage(message); + UpdateHandshakeMessagesDigest(message); + } + + internal byte[] ReceiveMessageBody(byte msg_type) + { + Message message = ReceiveMessage(); + if (message.Type != msg_type) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + return message.Body; + } + + internal Message ReceiveMessage() + { + if (mSending) + { + mSending = false; + PrepareInboundFlight(); + } + + // Check if we already have the next message waiting + { + DtlsReassembler next = (DtlsReassembler)mCurrentInboundFlight[mNextReceiveSeq]; + if (next != null) + { + byte[] body = next.GetBodyIfComplete(); + if (body != null) + { + mPreviousInboundFlight = null; + return UpdateHandshakeMessagesDigest(new Message(mNextReceiveSeq++, next.MsgType, body)); + } + } + } + + byte[] buf = null; + + // TODO Check the conditions under which we should reset this + int readTimeoutMillis = 1000; + + for (;;) + { + int receiveLimit = mRecordLayer.GetReceiveLimit(); + if (buf == null || buf.Length < receiveLimit) + { + buf = new byte[receiveLimit]; + } + + // TODO Handle records containing multiple handshake messages + + try + { + for (; ; ) + { + int received = mRecordLayer.Receive(buf, 0, receiveLimit, readTimeoutMillis); + if (received < 0) + { + break; + } + if (received < 12) + { + continue; + } + int fragment_length = TlsUtilities.ReadUint24(buf, 9); + if (received != (fragment_length + 12)) + { + continue; + } + int seq = TlsUtilities.ReadUint16(buf, 4); + if (seq > (mNextReceiveSeq + MAX_RECEIVE_AHEAD)) + { + continue; + } + byte msg_type = TlsUtilities.ReadUint8(buf, 0); + int length = TlsUtilities.ReadUint24(buf, 1); + int fragment_offset = TlsUtilities.ReadUint24(buf, 6); + if (fragment_offset + fragment_length > length) + { + continue; + } + + if (seq < mNextReceiveSeq) + { + /* + * NOTE: If we Receive the previous flight of incoming messages in full + * again, retransmit our last flight + */ + if (mPreviousInboundFlight != null) + { + DtlsReassembler reassembler = (DtlsReassembler)mPreviousInboundFlight[seq]; + if (reassembler != null) + { + reassembler.ContributeFragment(msg_type, length, buf, 12, fragment_offset, + fragment_length); + + if (CheckAll(mPreviousInboundFlight)) + { + ResendOutboundFlight(); + + /* + * TODO[DTLS] implementations SHOULD back off handshake packet + * size during the retransmit backoff. + */ + readTimeoutMillis = System.Math.Min(readTimeoutMillis * 2, 60000); + + ResetAll(mPreviousInboundFlight); + } + } + } + } + else + { + DtlsReassembler reassembler = (DtlsReassembler)mCurrentInboundFlight[seq]; + if (reassembler == null) + { + reassembler = new DtlsReassembler(msg_type, length); + mCurrentInboundFlight[seq] = reassembler; + } + + reassembler.ContributeFragment(msg_type, length, buf, 12, fragment_offset, fragment_length); + + if (seq == mNextReceiveSeq) + { + byte[] body = reassembler.GetBodyIfComplete(); + if (body != null) + { + mPreviousInboundFlight = null; + return UpdateHandshakeMessagesDigest(new Message(mNextReceiveSeq++, + reassembler.MsgType, body)); + } + } + } + } + } + catch (IOException) + { + // NOTE: Assume this is a timeout for the moment + } + + ResendOutboundFlight(); + + /* + * TODO[DTLS] implementations SHOULD back off handshake packet size during the + * retransmit backoff. + */ + readTimeoutMillis = System.Math.Min(readTimeoutMillis * 2, 60000); + } + } + + internal void Finish() + { + DtlsHandshakeRetransmit retransmit = null; + if (!mSending) + { + CheckInboundFlight(); + } + else if (mCurrentInboundFlight != null) + { + /* + * RFC 6347 4.2.4. In addition, for at least twice the default MSL defined for [TCP], + * when in the FINISHED state, the node that transmits the last flight (the server in an + * ordinary handshake or the client in a resumed handshake) MUST respond to a retransmit + * of the peer's last flight with a retransmit of the last flight. + */ + retransmit = new Retransmit(this); + } + + mRecordLayer.HandshakeSuccessful(retransmit); + } + + internal void ResetHandshakeMessagesDigest() + { + mHandshakeHash.Reset(); + } + + private void HandleRetransmittedHandshakeRecord(int epoch, byte[] buf, int off, int len) + { + /* + * TODO Need to handle the case where the previous inbound flight contains + * messages from two epochs. + */ + if (len < 12) + return; + int fragment_length = TlsUtilities.ReadUint24(buf, off + 9); + if (len != (fragment_length + 12)) + return; + int seq = TlsUtilities.ReadUint16(buf, off + 4); + if (seq >= mNextReceiveSeq) + return; + + byte msg_type = TlsUtilities.ReadUint8(buf, off); + + // TODO This is a hack that only works until we try to support renegotiation + int expectedEpoch = msg_type == HandshakeType.finished ? 1 : 0; + if (epoch != expectedEpoch) + return; + + int length = TlsUtilities.ReadUint24(buf, off + 1); + int fragment_offset = TlsUtilities.ReadUint24(buf, off + 6); + if (fragment_offset + fragment_length > length) + return; + + DtlsReassembler reassembler = (DtlsReassembler)mCurrentInboundFlight[seq]; + if (reassembler != null) + { + reassembler.ContributeFragment(msg_type, length, buf, off + 12, fragment_offset, + fragment_length); + if (CheckAll(mCurrentInboundFlight)) + { + ResendOutboundFlight(); + ResetAll(mCurrentInboundFlight); + } + } + } + + /** + * Check that there are no "extra" messages left in the current inbound flight + */ + private void CheckInboundFlight() + { + foreach (int key in mCurrentInboundFlight.Keys) + { + if (key >= mNextReceiveSeq) + { + // TODO Should this be considered an error? + } + } + } + + private void PrepareInboundFlight() + { + ResetAll(mCurrentInboundFlight); + mPreviousInboundFlight = mCurrentInboundFlight; + mCurrentInboundFlight = Platform.CreateHashtable(); + } + + private void ResendOutboundFlight() + { + mRecordLayer.ResetWriteEpoch(); + for (int i = 0; i < mOutboundFlight.Count; ++i) + { + WriteMessage((Message)mOutboundFlight[i]); + } + } + + private Message UpdateHandshakeMessagesDigest(Message message) + { + if (message.Type != HandshakeType.hello_request) + { + byte[] body = message.Body; + byte[] buf = new byte[12]; + TlsUtilities.WriteUint8(message.Type, buf, 0); + TlsUtilities.WriteUint24(body.Length, buf, 1); + TlsUtilities.WriteUint16(message.Seq, buf, 4); + TlsUtilities.WriteUint24(0, buf, 6); + TlsUtilities.WriteUint24(body.Length, buf, 9); + mHandshakeHash.BlockUpdate(buf, 0, buf.Length); + mHandshakeHash.BlockUpdate(body, 0, body.Length); + } + return message; + } + + private void WriteMessage(Message message) + { + int sendLimit = mRecordLayer.GetSendLimit(); + int fragmentLimit = sendLimit - 12; + + // TODO Support a higher minimum fragment size? + if (fragmentLimit < 1) + { + // TODO Should we be throwing an exception here? + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + int length = message.Body.Length; + + // NOTE: Must still send a fragment if body is empty + int fragment_offset = 0; + do + { + int fragment_length = System.Math.Min(length - fragment_offset, fragmentLimit); + WriteHandshakeFragment(message, fragment_offset, fragment_length); + fragment_offset += fragment_length; + } + while (fragment_offset < length); + } + + private void WriteHandshakeFragment(Message message, int fragment_offset, int fragment_length) + { + RecordLayerBuffer fragment = new RecordLayerBuffer(12 + fragment_length); + TlsUtilities.WriteUint8(message.Type, fragment); + TlsUtilities.WriteUint24(message.Body.Length, fragment); + TlsUtilities.WriteUint16(message.Seq, fragment); + TlsUtilities.WriteUint24(fragment_offset, fragment); + TlsUtilities.WriteUint24(fragment_length, fragment); + fragment.Write(message.Body, fragment_offset, fragment_length); + + fragment.SendToRecordLayer(mRecordLayer); + } + + private static bool CheckAll(IDictionary inboundFlight) + { + foreach (DtlsReassembler r in inboundFlight.Values) + { + if (r.GetBodyIfComplete() == null) + { + return false; + } + } + return true; + } + + private static void ResetAll(IDictionary inboundFlight) + { + foreach (DtlsReassembler r in inboundFlight.Values) + { + r.Reset(); + } + } + + internal class Message + { + private readonly int mMessageSeq; + private readonly byte mMsgType; + private readonly byte[] mBody; + + internal Message(int message_seq, byte msg_type, byte[] body) + { + this.mMessageSeq = message_seq; + this.mMsgType = msg_type; + this.mBody = body; + } + + public int Seq + { + get { return mMessageSeq; } + } + + public byte Type + { + get { return mMsgType; } + } + + public byte[] Body + { + get { return mBody; } + } + } + + internal class RecordLayerBuffer + : MemoryStream + { + internal RecordLayerBuffer(int size) + : base(size) + { + } + + internal void SendToRecordLayer(DtlsRecordLayer recordLayer) + { + recordLayer.Send(GetBuffer(), 0, (int)Length); + this.Close(); + } + } + + internal class Retransmit + : DtlsHandshakeRetransmit + { + private readonly DtlsReliableHandshake mOuter; + + internal Retransmit(DtlsReliableHandshake outer) + { + this.mOuter = outer; + } + + public void ReceivedHandshakeRecord(int epoch, byte[] buf, int off, int len) + { + mOuter.HandleRetransmittedHandshakeRecord(epoch, buf, off, len); + } + } + } +} diff --git a/crypto/src/crypto/tls/DtlsReplayWindow.cs b/crypto/src/crypto/tls/DtlsReplayWindow.cs new file mode 100644
index 000000000..ea18e805e --- /dev/null +++ b/crypto/src/crypto/tls/DtlsReplayWindow.cs
@@ -0,0 +1,85 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 4347 4.1.2.5 Anti-replay + * <p/> + * Support fast rejection of duplicate records by maintaining a sliding receive window + */ + internal class DtlsReplayWindow + { + private const long VALID_SEQ_MASK = 0x0000FFFFFFFFFFFFL; + + private const long WINDOW_SIZE = 64L; + + private long mLatestConfirmedSeq = -1; + private long mBitmap = 0; + + /** + * Check whether a received record with the given sequence number should be rejected as a duplicate. + * + * @param seq the 48-bit DTLSPlainText.sequence_number field of a received record. + * @return true if the record should be discarded without further processing. + */ + internal bool ShouldDiscard(long seq) + { + if ((seq & VALID_SEQ_MASK) != seq) + return true; + + if (seq <= mLatestConfirmedSeq) + { + long diff = mLatestConfirmedSeq - seq; + if (diff >= WINDOW_SIZE) + return true; + if ((mBitmap & (1L << (int)diff)) != 0) + return true; + } + + return false; + } + + /** + * Report that a received record with the given sequence number passed authentication checks. + * + * @param seq the 48-bit DTLSPlainText.sequence_number field of an authenticated record. + */ + internal void ReportAuthenticated(long seq) + { + if ((seq & VALID_SEQ_MASK) != seq) + throw new ArgumentException("out of range", "seq"); + + if (seq <= mLatestConfirmedSeq) + { + long diff = mLatestConfirmedSeq - seq; + if (diff < WINDOW_SIZE) + { + mBitmap |= (1L << (int)diff); + } + } + else + { + long diff = seq - mLatestConfirmedSeq; + if (diff >= WINDOW_SIZE) + { + mBitmap = 1; + } + else + { + mBitmap <<= (int)diff; + mBitmap |= 1; + } + mLatestConfirmedSeq = seq; + } + } + + /** + * When a new epoch begins, sequence numbers begin again at 0 + */ + internal void Reset() + { + mLatestConfirmedSeq = -1; + mBitmap = 0; + } + } +} diff --git a/crypto/src/crypto/tls/DtlsServerProtocol.cs b/crypto/src/crypto/tls/DtlsServerProtocol.cs new file mode 100644
index 000000000..9c7caf290 --- /dev/null +++ b/crypto/src/crypto/tls/DtlsServerProtocol.cs
@@ -0,0 +1,655 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DtlsServerProtocol + : DtlsProtocol + { + protected bool mVerifyRequests = true; + + public DtlsServerProtocol(SecureRandom secureRandom) + : base(secureRandom) + { + } + + public virtual bool VerifyRequests + { + get { return mVerifyRequests; } + set { this.mVerifyRequests = value; } + } + + public virtual DtlsTransport Accept(TlsServer server, DatagramTransport transport) + { + if (server == null) + throw new ArgumentNullException("server"); + if (transport == null) + throw new ArgumentNullException("transport"); + + SecurityParameters securityParameters = new SecurityParameters(); + securityParameters.entity = ConnectionEnd.server; + + ServerHandshakeState state = new ServerHandshakeState(); + state.server = server; + state.serverContext = new TlsServerContextImpl(mSecureRandom, securityParameters); + + securityParameters.serverRandom = TlsProtocol.CreateRandomBlock(server.ShouldUseGmtUnixTime(), + state.serverContext.NonceRandomGenerator); + + server.Init(state.serverContext); + + DtlsRecordLayer recordLayer = new DtlsRecordLayer(transport, state.serverContext, server, ContentType.handshake); + + // TODO Need to handle sending of HelloVerifyRequest without entering a full connection + + try + { + return ServerHandshake(state, recordLayer); + } + catch (TlsFatalAlert fatalAlert) + { + recordLayer.Fail(fatalAlert.AlertDescription); + throw fatalAlert; + } + catch (IOException e) + { + recordLayer.Fail(AlertDescription.internal_error); + throw e; + } + catch (Exception e) + { + recordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer) + { + SecurityParameters securityParameters = state.serverContext.SecurityParameters; + DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.serverContext, recordLayer); + + DtlsReliableHandshake.Message clientMessage = handshake.ReceiveMessage(); + + { + // NOTE: After receiving a record from the client, we discover the record layer version + ProtocolVersion client_version = recordLayer.DiscoveredPeerVersion; + // TODO Read RFCs for guidance on the expected record layer version number + state.serverContext.SetClientVersion(client_version); + } + + if (clientMessage.Type == HandshakeType.client_hello) + { + ProcessClientHello(state, clientMessage.Body); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + { + byte[] serverHelloBody = GenerateServerHello(state); + + ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.maxFragmentLength); + + handshake.SendMessage(HandshakeType.server_hello, serverHelloBody); + } + + handshake.NotifyHelloComplete(); + + IList serverSupplementalData = state.server.GetServerSupplementalData(); + if (serverSupplementalData != null) + { + byte[] supplementalDataBody = GenerateSupplementalData(serverSupplementalData); + handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody); + } + + state.keyExchange = state.server.GetKeyExchange(); + state.keyExchange.Init(state.serverContext); + + state.serverCredentials = state.server.GetCredentials(); + + Certificate serverCertificate = null; + + if (state.serverCredentials == null) + { + state.keyExchange.SkipServerCredentials(); + } + else + { + state.keyExchange.ProcessServerCredentials(state.serverCredentials); + + serverCertificate = state.serverCredentials.Certificate; + byte[] certificateBody = GenerateCertificate(serverCertificate); + handshake.SendMessage(HandshakeType.certificate, certificateBody); + } + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.IsEmpty) + { + state.allowCertificateStatus = false; + } + + if (state.allowCertificateStatus) + { + CertificateStatus certificateStatus = state.server.GetCertificateStatus(); + if (certificateStatus != null) + { + byte[] certificateStatusBody = GenerateCertificateStatus(state, certificateStatus); + handshake.SendMessage(HandshakeType.certificate_status, certificateStatusBody); + } + } + + byte[] serverKeyExchange = state.keyExchange.GenerateServerKeyExchange(); + if (serverKeyExchange != null) + { + handshake.SendMessage(HandshakeType.server_key_exchange, serverKeyExchange); + } + + if (state.serverCredentials != null) + { + state.certificateRequest = state.server.GetCertificateRequest(); + if (state.certificateRequest != null) + { + state.keyExchange.ValidateCertificateRequest(state.certificateRequest); + + byte[] certificateRequestBody = GenerateCertificateRequest(state, state.certificateRequest); + handshake.SendMessage(HandshakeType.certificate_request, certificateRequestBody); + + TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, + state.certificateRequest.SupportedSignatureAlgorithms); + } + } + + handshake.SendMessage(HandshakeType.server_hello_done, TlsUtilities.EmptyBytes); + + handshake.HandshakeHash.SealHashAlgorithms(); + + clientMessage = handshake.ReceiveMessage(); + + if (clientMessage.Type == HandshakeType.supplemental_data) + { + ProcessClientSupplementalData(state, clientMessage.Body); + clientMessage = handshake.ReceiveMessage(); + } + else + { + state.server.ProcessClientSupplementalData(null); + } + + if (state.certificateRequest == null) + { + state.keyExchange.SkipClientCredentials(); + } + else + { + if (clientMessage.Type == HandshakeType.certificate) + { + ProcessClientCertificate(state, clientMessage.Body); + clientMessage = handshake.ReceiveMessage(); + } + else + { + if (TlsUtilities.IsTlsV12(state.serverContext)) + { + /* + * RFC 5246 If no suitable certificate is available, the client MUST send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + NotifyClientCertificate(state, Certificate.EmptyChain); + } + } + + if (clientMessage.Type == HandshakeType.client_key_exchange) + { + ProcessClientKeyExchange(state, clientMessage.Body); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish(); + securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.serverContext, prepareFinishHash, null); + + TlsProtocol.EstablishMasterSecret(state.serverContext, state.keyExchange); + recordLayer.InitPendingEpoch(state.server.GetCipher()); + + /* + * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing + * capability (i.e., all certificates except those containing fixed Diffie-Hellman + * parameters). + */ + if (ExpectCertificateVerifyMessage(state)) + { + byte[] certificateVerifyBody = handshake.ReceiveMessageBody(HandshakeType.certificate_verify); + ProcessCertificateVerify(state, certificateVerifyBody, prepareFinishHash); + } + + // NOTE: Calculated exclusive of the actual Finished message from the client + byte[] expectedClientVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.client_finished, + TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null)); + ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedClientVerifyData); + + if (state.expectSessionTicket) + { + NewSessionTicket newSessionTicket = state.server.GetNewSessionTicket(); + byte[] newSessionTicketBody = GenerateNewSessionTicket(state, newSessionTicket); + handshake.SendMessage(HandshakeType.session_ticket, newSessionTicketBody); + } + + // NOTE: Calculated exclusive of the Finished message itself + byte[] serverVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.server_finished, + TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null)); + handshake.SendMessage(HandshakeType.finished, serverVerifyData); + + handshake.Finish(); + + state.server.NotifyHandshakeComplete(); + + return new DtlsTransport(recordLayer); + } + + protected virtual byte[] GenerateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest) + { + MemoryStream buf = new MemoryStream(); + certificateRequest.Encode(buf); + return buf.ToArray(); + } + + protected virtual byte[] GenerateCertificateStatus(ServerHandshakeState state, CertificateStatus certificateStatus) + { + MemoryStream buf = new MemoryStream(); + certificateStatus.Encode(buf); + return buf.ToArray(); + } + + protected virtual byte[] GenerateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket) + { + MemoryStream buf = new MemoryStream(); + newSessionTicket.Encode(buf); + return buf.ToArray(); + } + + protected virtual byte[] GenerateServerHello(ServerHandshakeState state) + { + SecurityParameters securityParameters = state.serverContext.SecurityParameters; + + MemoryStream buf = new MemoryStream(); + + { + ProtocolVersion server_version = state.server.GetServerVersion(); + if (!server_version.IsEqualOrEarlierVersionOf(state.serverContext.ClientVersion)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + // TODO Read RFCs for guidance on the expected record layer version number + // recordStream.setReadVersion(server_version); + // recordStream.setWriteVersion(server_version); + // recordStream.setRestrictReadVersion(true); + state.serverContext.SetServerVersion(server_version); + + TlsUtilities.WriteVersion(state.serverContext.ServerVersion, buf); + } + + buf.Write(securityParameters.ServerRandom, 0, securityParameters.ServerRandom.Length); + + /* + * The server may return an empty session_id to indicate that the session will not be cached + * and therefore cannot be resumed. + */ + TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); + + int selectedCipherSuite = state.server.GetSelectedCipherSuite(); + if (!Arrays.Contains(state.offeredCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || CipherSuite.IsScsv(selectedCipherSuite) + || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, state.serverContext.ServerVersion)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + ValidateSelectedCipherSuite(selectedCipherSuite, AlertDescription.internal_error); + securityParameters.cipherSuite = selectedCipherSuite; + + byte selectedCompressionMethod = state.server.GetSelectedCompressionMethod(); + if (!Arrays.Contains(state.offeredCompressionMethods, selectedCompressionMethod)) + throw new TlsFatalAlert(AlertDescription.internal_error); + securityParameters.compressionAlgorithm = selectedCompressionMethod; + + TlsUtilities.WriteUint16(selectedCipherSuite, buf); + TlsUtilities.WriteUint8(selectedCompressionMethod, buf); + + state.serverExtensions = state.server.GetServerExtensions(); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + if (state.secure_renegotiation) + { + byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions, ExtensionType.renegotiation_info); + bool noRenegExt = (null == renegExtData); + + if (noRenegExt) + { + /* + * Note that sending a "renegotiation_info" extension in response to a ClientHello + * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, + * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed + * because the client is signaling its willingness to receive the extension via the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + + /* + * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty + * "renegotiation_info" extension in the ServerHello message. + */ + state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions); + state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes); + } + } + + if (securityParameters.extendedMasterSecret) + { + state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions); + TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions); + } + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + + if (state.serverExtensions != null) + { + securityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(state.serverExtensions); + + securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession, + state.clientExtensions, state.serverExtensions, AlertDescription.internal_error); + + securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(state.serverExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + state.allowCertificateStatus = !state.resumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.status_request, + AlertDescription.internal_error); + + state.expectSessionTicket = !state.resumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.session_ticket, + AlertDescription.internal_error); + + TlsProtocol.WriteExtensions(buf, state.serverExtensions); + } + + securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.serverContext, + securityParameters.CipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length + * has a verify_data_length equal to 12. This includes all existing cipher suites. + */ + securityParameters.verifyDataLength = 12; + + return buf.ToArray(); + } + + protected virtual void NotifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate) + { + if (state.certificateRequest == null) + throw new InvalidOperationException(); + + if (state.clientCertificate != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + state.clientCertificate = clientCertificate; + + if (clientCertificate.IsEmpty) + { + state.keyExchange.SkipClientCredentials(); + } + else + { + + /* + * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request + * message was non-empty, one of the certificates in the certificate chain SHOULD be + * issued by one of the listed CAs. + */ + + state.clientCertificateType = TlsUtilities.GetClientCertificateType(clientCertificate, + state.serverCredentials.Certificate); + + state.keyExchange.ProcessClientCertificate(clientCertificate); + } + + /* + * RFC 5246 7.4.6. If the client does not send any certificates, the server MAY at its + * discretion either continue the handshake without client authentication, or respond with a + * fatal handshake_failure alert. Also, if some aspect of the certificate chain was + * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its + * discretion either continue the handshake (considering the client unauthenticated) or send + * a fatal alert. + */ + state.server.NotifyClientCertificate(clientCertificate); + } + + protected virtual void ProcessClientCertificate(ServerHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + Certificate clientCertificate = Certificate.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + NotifyClientCertificate(state, clientCertificate); + } + + protected virtual void ProcessCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash) + { + MemoryStream buf = new MemoryStream(body, false); + + TlsServerContextImpl context = state.serverContext; + DigitallySigned clientCertificateVerify = DigitallySigned.Parse(context, buf); + + TlsProtocol.AssertEmpty(buf); + + // Verify the CertificateVerify message contains a correct signature. + bool verified = false; + try + { + byte[] hash; + if (TlsUtilities.IsTlsV12(context)) + { + hash = prepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + } + else + { + hash = context.SecurityParameters.SessionHash; + } + + X509CertificateStructure x509Cert = state.clientCertificate.GetCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo); + + TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)state.clientCertificateType); + tlsSigner.Init(context); + verified = tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, + clientCertificateVerify.Signature, publicKey, hash); + } + catch (Exception) + { + } + + if (!verified) + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + + protected virtual void ProcessClientHello(ServerHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + // TODO Read RFCs for guidance on the expected record layer version number + ProtocolVersion client_version = TlsUtilities.ReadVersion(buf); + if (!client_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + /* + * Read the client random + */ + byte[] client_random = TlsUtilities.ReadFully(32, buf); + + byte[] sessionID = TlsUtilities.ReadOpaque8(buf); + if (sessionID.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347 + byte[] cookie = TlsUtilities.ReadOpaque8(buf); + + int cipher_suites_length = TlsUtilities.ReadUint16(buf); + if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + /* + * NOTE: "If the session_id field is not empty (implying a session resumption request) this + * vector must include at least the cipher_suite from that session." + */ + state.offeredCipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, buf); + + int compression_methods_length = TlsUtilities.ReadUint8(buf); + if (compression_methods_length < 1) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + state.offeredCompressionMethods = TlsUtilities.ReadUint8Array(compression_methods_length, buf); + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + state.clientExtensions = TlsProtocol.ReadExtensions(buf); + + TlsServerContextImpl context = state.serverContext; + SecurityParameters securityParameters = context.SecurityParameters; + + /* + * TODO[session-hash] + * + * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes + * that do not use the extended master secret [..]. (and see 5.2, 5.3) + */ + securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions); + + context.SetClientVersion(client_version); + + state.server.NotifyClientVersion(client_version); + state.server.NotifyFallback(Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)); + + securityParameters.clientRandom = client_random; + + state.server.NotifyOfferedCipherSuites(state.offeredCipherSuites); + state.server.NotifyOfferedCompressionMethods(state.offeredCompressionMethods); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + + /* + * When a ClientHello is received, the server MUST check if it includes the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag + * to TRUE. + */ + if (Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + { + state.secure_renegotiation = true; + } + + /* + * The server MUST check if the "renegotiation_info" extension is included in the + * ClientHello. + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info); + if (renegExtData != null) + { + /* + * If the extension is present, set secure_renegotiation flag to TRUE. The + * server MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake. + */ + state.secure_renegotiation = true; + + if (!Arrays.ConstantTimeAreEqual(renegExtData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + state.server.NotifySecureRenegotiation(state.secure_renegotiation); + + if (state.clientExtensions != null) + { + state.server.ProcessClientExtensions(state.clientExtensions); + } + } + + protected virtual void ProcessClientKeyExchange(ServerHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + + state.keyExchange.ProcessClientKeyExchange(buf); + + TlsProtocol.AssertEmpty(buf); + } + + protected virtual void ProcessClientSupplementalData(ServerHandshakeState state, byte[] body) + { + MemoryStream buf = new MemoryStream(body, false); + IList clientSupplementalData = TlsProtocol.ReadSupplementalDataMessage(buf); + state.server.ProcessClientSupplementalData(clientSupplementalData); + } + + protected virtual bool ExpectCertificateVerifyMessage(ServerHandshakeState state) + { + return state.clientCertificateType >= 0 && TlsUtilities.HasSigningCapability((byte)state.clientCertificateType); + } + + protected internal class ServerHandshakeState + { + internal TlsServer server = null; + internal TlsServerContextImpl serverContext = null; + internal int[] offeredCipherSuites = null; + internal byte[] offeredCompressionMethods = null; + internal IDictionary clientExtensions = null; + internal IDictionary serverExtensions = null; + internal bool resumedSession = false; + internal bool secure_renegotiation = false; + internal bool allowCertificateStatus = false; + internal bool expectSessionTicket = false; + internal TlsKeyExchange keyExchange = null; + internal TlsCredentials serverCredentials = null; + internal CertificateRequest certificateRequest = null; + internal short clientCertificateType = -1; + internal Certificate clientCertificate = null; + } + } +} diff --git a/crypto/src/crypto/tls/DtlsTransport.cs b/crypto/src/crypto/tls/DtlsTransport.cs new file mode 100644
index 000000000..5c607336b --- /dev/null +++ b/crypto/src/crypto/tls/DtlsTransport.cs
@@ -0,0 +1,77 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DtlsTransport + : DatagramTransport + { + private readonly DtlsRecordLayer mRecordLayer; + + internal DtlsTransport(DtlsRecordLayer recordLayer) + { + this.mRecordLayer = recordLayer; + } + + public virtual int GetReceiveLimit() + { + return mRecordLayer.GetReceiveLimit(); + } + + public virtual int GetSendLimit() + { + return mRecordLayer.GetSendLimit(); + } + + public virtual int Receive(byte[] buf, int off, int len, int waitMillis) + { + try + { + return mRecordLayer.Receive(buf, off, len, waitMillis); + } + catch (TlsFatalAlert fatalAlert) + { + mRecordLayer.Fail(fatalAlert.AlertDescription); + throw fatalAlert; + } + catch (IOException e) + { + mRecordLayer.Fail(AlertDescription.internal_error); + throw e; + } + catch (Exception e) + { + mRecordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + public virtual void Send(byte[] buf, int off, int len) + { + try + { + mRecordLayer.Send(buf, off, len); + } + catch (TlsFatalAlert fatalAlert) + { + mRecordLayer.Fail(fatalAlert.AlertDescription); + throw fatalAlert; + } + catch (IOException e) + { + mRecordLayer.Fail(AlertDescription.internal_error); + throw e; + } + catch (Exception e) + { + mRecordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + public virtual void Close() + { + mRecordLayer.Close(); + } + } +} diff --git a/crypto/src/crypto/tls/ECBasisType.cs b/crypto/src/crypto/tls/ECBasisType.cs
index b7c9c6bd7..5416e17c0 100644 --- a/crypto/src/crypto/tls/ECBasisType.cs +++ b/crypto/src/crypto/tls/ECBasisType.cs
@@ -5,10 +5,10 @@ namespace Org.BouncyCastle.Crypto.Tls /// <summary>RFC 4492 5.4. (Errata ID: 2389)</summary> public abstract class ECBasisType { - public const short ec_basis_trinomial = 1; - public const short ec_basis_pentanomial = 2; + public const byte ec_basis_trinomial = 1; + public const byte ec_basis_pentanomial = 2; - public static bool IsValid(short ecBasisType) + public static bool IsValid(byte ecBasisType) { return ecBasisType >= ec_basis_trinomial && ecBasisType <= ec_basis_pentanomial; } diff --git a/crypto/src/crypto/tls/EncryptionAlgorithm.cs b/crypto/src/crypto/tls/EncryptionAlgorithm.cs
index dbeaa3356..05d1c5d7a 100644 --- a/crypto/src/crypto/tls/EncryptionAlgorithm.cs +++ b/crypto/src/crypto/tls/EncryptionAlgorithm.cs
@@ -50,9 +50,20 @@ namespace Org.BouncyCastle.Crypto.Tls public const int AES_256_CCM_8 = 18; /* - * TBD[draft-josefsson-salsa20-tls-02] + * RFC 6367 */ - const int ESTREAM_SALSA20 = 100; - const int SALSA20 = 101; + public const int CAMELLIA_128_GCM = 19; + public const int CAMELLIA_256_GCM = 20; + + /* + * draft-josefsson-salsa20-tls-04 + */ + public const int ESTREAM_SALSA20 = 100; + public const int SALSA20 = 101; + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + public const int AEAD_CHACHA20_POLY1305 = 102; } } diff --git a/crypto/src/crypto/tls/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs
index c186d410b..5970769d7 100644 --- a/crypto/src/crypto/tls/ExporterLabel.cs +++ b/crypto/src/crypto/tls/ExporterLabel.cs
@@ -6,12 +6,6 @@ namespace Org.BouncyCastle.Crypto.Tls public abstract class ExporterLabel { /* - * BC-specific - */ - internal const string client_random = "client random"; - internal const string server_random = "server random"; - - /* * RFC 5246 */ public const string client_finished = "client finished"; @@ -34,5 +28,10 @@ namespace Org.BouncyCastle.Crypto.Tls * RFC 5764 */ public const string dtls_srtp = "EXTRACTOR-dtls_srtp"; + + /* + * draft-ietf-tls-session-hash-04 + */ + public static readonly string extended_master_secret = "extended master secret"; } } diff --git a/crypto/src/crypto/tls/ExtensionType.cs b/crypto/src/crypto/tls/ExtensionType.cs
index 4e265c358..b4b24f7c3 100644 --- a/crypto/src/crypto/tls/ExtensionType.cs +++ b/crypto/src/crypto/tls/ExtensionType.cs
@@ -44,16 +44,28 @@ namespace Org.BouncyCastle.Crypto.Tls public const int heartbeat = 15; /* + * RFC 7366 + */ + public const int encrypt_then_mac = 22; + + /* + * draft-ietf-tls-session-hash-04 + * + * NOTE: Early code-point assignment + */ + public const int extended_master_secret = 23; + + /* * RFC 5077 7. */ public const int session_ticket = 35; /* - * draft-gutmann-tls-encrypt-then-mac-05 + * draft-ietf-tls-negotiated-ff-dhe-01 * - * NOTE: This value has not yet been reserved by the IETF + * WARNING: Placeholder value; the real value is TBA */ - public static readonly int encrypt_then_mac = 66; + public static readonly int negotiated_ff_dhe_groups = 101; /* * RFC 5746 3.2. diff --git a/crypto/src/crypto/tls/FiniteFieldDheGroup.cs b/crypto/src/crypto/tls/FiniteFieldDheGroup.cs new file mode 100644
index 000000000..437504941 --- /dev/null +++ b/crypto/src/crypto/tls/FiniteFieldDheGroup.cs
@@ -0,0 +1,21 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /* + * draft-ietf-tls-negotiated-ff-dhe-01 + */ + public abstract class FiniteFieldDheGroup + { + public const byte ffdhe2432 = 0; + public const byte ffdhe3072 = 1; + public const byte ffdhe4096 = 2; + public const byte ffdhe6144 = 3; + public const byte ffdhe8192 = 4; + + public static bool IsValid(byte group) + { + return group >= ffdhe2432 && group <= ffdhe8192; + } + } +} diff --git a/crypto/src/crypto/tls/HeartbeatExtension.cs b/crypto/src/crypto/tls/HeartbeatExtension.cs new file mode 100644
index 000000000..049837266 --- /dev/null +++ b/crypto/src/crypto/tls/HeartbeatExtension.cs
@@ -0,0 +1,52 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class HeartbeatExtension + { + protected readonly byte mMode; + + public HeartbeatExtension(byte mode) + { + if (!HeartbeatMode.IsValid(mode)) + throw new ArgumentException("not a valid HeartbeatMode value", "mode"); + + this.mMode = mode; + } + + public virtual byte Mode + { + get { return mMode; } + } + + /** + * Encode this {@link HeartbeatExtension} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mMode, output); + } + + /** + * Parse a {@link HeartbeatExtension} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link HeartbeatExtension} object. + * @throws IOException + */ + public static HeartbeatExtension Parse(Stream input) + { + byte mode = TlsUtilities.ReadUint8(input); + if (!HeartbeatMode.IsValid(mode)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return new HeartbeatExtension(mode); + } + } +} diff --git a/crypto/src/crypto/tls/HeartbeatMessage.cs b/crypto/src/crypto/tls/HeartbeatMessage.cs new file mode 100644
index 000000000..f64a7baa4 --- /dev/null +++ b/crypto/src/crypto/tls/HeartbeatMessage.cs
@@ -0,0 +1,102 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class HeartbeatMessage + { + protected readonly byte mType; + protected readonly byte[] mPayload; + protected readonly int mPaddingLength; + + public HeartbeatMessage(byte type, byte[] payload, int paddingLength) + { + if (!HeartbeatMessageType.IsValid(type)) + throw new ArgumentException("not a valid HeartbeatMessageType value", "type"); + if (payload == null || payload.Length >= (1 << 16)) + throw new ArgumentException("must have length < 2^16", "payload"); + if (paddingLength < 16) + throw new ArgumentException("must be at least 16", "paddingLength"); + + this.mType = type; + this.mPayload = payload; + this.mPaddingLength = paddingLength; + } + + /** + * Encode this {@link HeartbeatMessage} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(TlsContext context, Stream output) + { + TlsUtilities.WriteUint8(mType, output); + + TlsUtilities.CheckUint16(mPayload.Length); + TlsUtilities.WriteUint16(mPayload.Length, output); + output.Write(mPayload, 0, mPayload.Length); + + byte[] padding = new byte[mPaddingLength]; + context.NonceRandomGenerator.NextBytes(padding); + output.Write(padding, 0, padding.Length); + } + + /** + * Parse a {@link HeartbeatMessage} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link HeartbeatMessage} object. + * @throws IOException + */ + public static HeartbeatMessage Parse(Stream input) + { + byte type = TlsUtilities.ReadUint8(input); + if (!HeartbeatMessageType.IsValid(type)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + int payload_length = TlsUtilities.ReadUint16(input); + + PayloadBuffer buf = new PayloadBuffer(); + Streams.PipeAll(input, buf); + + byte[] payload = buf.ToTruncatedByteArray(payload_length); + if (payload == null) + { + /* + * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the + * received HeartbeatMessage MUST be discarded silently. + */ + return null; + } + + TlsUtilities.CheckUint16(buf.Length); + int padding_length = (int)buf.Length - payload.Length; + + /* + * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored + */ + return new HeartbeatMessage(type, payload, padding_length); + } + + internal class PayloadBuffer + : MemoryStream + { + internal byte[] ToTruncatedByteArray(int payloadLength) + { + /* + * RFC 6520 4. The padding_length MUST be at least 16. + */ + int minimumCount = payloadLength + 16; + if (Length < minimumCount) + return null; + return Arrays.CopyOf(GetBuffer(), payloadLength); + } + } + } +} diff --git a/crypto/src/crypto/tls/HeartbeatMessageType.cs b/crypto/src/crypto/tls/HeartbeatMessageType.cs
index 9e3ad213c..57a4b86be 100644 --- a/crypto/src/crypto/tls/HeartbeatMessageType.cs +++ b/crypto/src/crypto/tls/HeartbeatMessageType.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls */ public abstract class HeartbeatMessageType { - public const short heartbeat_request = 1; - public const short heartbeat_response = 2; + public const byte heartbeat_request = 1; + public const byte heartbeat_response = 2; - public static bool IsValid(short heartbeatMessageType) + public static bool IsValid(byte heartbeatMessageType) { return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response; } diff --git a/crypto/src/crypto/tls/HeartbeatMode.cs b/crypto/src/crypto/tls/HeartbeatMode.cs
index 0968f6e10..f1570a84d 100644 --- a/crypto/src/crypto/tls/HeartbeatMode.cs +++ b/crypto/src/crypto/tls/HeartbeatMode.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls */ public abstract class HeartbeatMode { - public const short peer_allowed_to_send = 1; - public const short peer_not_allowed_to_send = 2; + public const byte peer_allowed_to_send = 1; + public const byte peer_not_allowed_to_send = 2; - public static bool IsValid(short heartbeatMode) + public static bool IsValid(byte heartbeatMode) { return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send; } diff --git a/crypto/src/crypto/tls/ICertificateVerifyer.cs b/crypto/src/crypto/tls/ICertificateVerifyer.cs deleted file mode 100644
index df5ea51d7..000000000 --- a/crypto/src/crypto/tls/ICertificateVerifyer.cs +++ /dev/null
@@ -1,18 +0,0 @@ -using System; - -using Org.BouncyCastle.Asn1.X509; - -namespace Org.BouncyCastle.Crypto.Tls -{ - /// <remarks> - /// This should be implemented by any class which can find out, if a given - /// certificate chain is being accepted by an client. - /// </remarks> - [Obsolete("Perform certificate verification in TlsAuthentication implementation")] - public interface ICertificateVerifyer - { - /// <param name="certs">The certs, which are part of the chain.</param> - /// <returns>True, if the chain is accepted, false otherwise</returns> - bool IsValid(X509CertificateStructure[] certs); - } -} diff --git a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs b/crypto/src/crypto/tls/LegacyTlsAuthentication.cs deleted file mode 100644
index bce31c0b0..000000000 --- a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs +++ /dev/null
@@ -1,30 +0,0 @@ -using System; - -namespace Org.BouncyCastle.Crypto.Tls -{ - /// <summary> - /// A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication. - /// </summary> - [Obsolete] - public class LegacyTlsAuthentication - : TlsAuthentication - { - protected ICertificateVerifyer verifyer; - - public LegacyTlsAuthentication(ICertificateVerifyer verifyer) - { - this.verifyer = verifyer; - } - - public virtual void NotifyServerCertificate(Certificate serverCertificate) - { - if (!this.verifyer.IsValid(serverCertificate.GetCertificateList())) - throw new TlsFatalAlert(AlertDescription.user_canceled); - } - - public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) - { - return null; - } - } -} diff --git a/crypto/src/crypto/tls/LegacyTlsClient.cs b/crypto/src/crypto/tls/LegacyTlsClient.cs deleted file mode 100644
index fbb9a732e..000000000 --- a/crypto/src/crypto/tls/LegacyTlsClient.cs +++ /dev/null
@@ -1,26 +0,0 @@ -using System; - -namespace Org.BouncyCastle.Crypto.Tls -{ - /// <summary> - /// A temporary class to use LegacyTlsAuthentication - /// </summary> - [Obsolete] - public class LegacyTlsClient - : DefaultTlsClient - { - [Obsolete] - protected ICertificateVerifyer verifyer; - - [Obsolete] - public LegacyTlsClient(ICertificateVerifyer verifyer) - { - this.verifyer = verifyer; - } - - public override TlsAuthentication GetAuthentication() - { - return new LegacyTlsAuthentication(verifyer); - } - } -} \ No newline at end of file diff --git a/crypto/src/crypto/tls/MaxFragmentLength.cs b/crypto/src/crypto/tls/MaxFragmentLength.cs
index adb6d129c..5b10b35dd 100644 --- a/crypto/src/crypto/tls/MaxFragmentLength.cs +++ b/crypto/src/crypto/tls/MaxFragmentLength.cs
@@ -7,12 +7,12 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 3546 3.2. */ - public const short pow2_9 = 1; - public const short pow2_10 = 2; - public const short pow2_11 = 3; - public const short pow2_12 = 4; + public const byte pow2_9 = 1; + public const byte pow2_10 = 2; + public const byte pow2_11 = 3; + public const byte pow2_12 = 4; - public static bool IsValid(short maxFragmentLength) + public static bool IsValid(byte maxFragmentLength) { return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12; } diff --git a/crypto/src/crypto/tls/NameType.cs b/crypto/src/crypto/tls/NameType.cs
index ffcb639d0..25f6046fc 100644 --- a/crypto/src/crypto/tls/NameType.cs +++ b/crypto/src/crypto/tls/NameType.cs
@@ -7,6 +7,6 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 3546 3.1. */ - public const short host_name = 0; + public const byte host_name = 0; } } diff --git a/crypto/src/crypto/tls/NamedCurve.cs b/crypto/src/crypto/tls/NamedCurve.cs
index 8ef395069..b8aa0ecde 100644 --- a/crypto/src/crypto/tls/NamedCurve.cs +++ b/crypto/src/crypto/tls/NamedCurve.cs
@@ -58,7 +58,8 @@ namespace Org.BouncyCastle.Crypto.Tls public static bool IsValid(int namedCurve) { - return namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1; + return (namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1) + || (namedCurve >= arbitrary_explicit_prime_curves && namedCurve <= arbitrary_explicit_char2_curves); } public static bool RefersToASpecificNamedCurve(int namedCurve) @@ -73,24 +74,4 @@ namespace Org.BouncyCastle.Crypto.Tls } } } - - internal class NamedCurveHelper - { - internal static ECDomainParameters GetECParameters(int namedCurve) - { - if (!NamedCurve.IsValid(namedCurve)) - return null; - - string curveName = namedCurve.ToString(); - - // Lazily created the first time a particular curve is accessed - X9ECParameters ecP = SecNamedCurves.GetByName(curveName); - - if (ecP == null) - return null; - - // It's a bit inefficient to do this conversion every time - return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); - } - } } diff --git a/crypto/src/crypto/tls/NewSessionTicket.cs b/crypto/src/crypto/tls/NewSessionTicket.cs new file mode 100644
index 000000000..a84026b8c --- /dev/null +++ b/crypto/src/crypto/tls/NewSessionTicket.cs
@@ -0,0 +1,53 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class NewSessionTicket + { + protected readonly long mTicketLifetimeHint; + protected readonly byte[] mTicket; + + public NewSessionTicket(long ticketLifetimeHint, byte[] ticket) + { + this.mTicketLifetimeHint = ticketLifetimeHint; + this.mTicket = ticket; + } + + public virtual long TicketLifetimeHint + { + get { return mTicketLifetimeHint; } + } + + public virtual byte[] Ticket + { + get { return mTicket; } + } + + /** + * Encode this {@link NewSessionTicket} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint32(mTicketLifetimeHint, output); + TlsUtilities.WriteOpaque16(mTicket, output); + } + + /** + * Parse a {@link NewSessionTicket} from a {@link Stream}. + * + * @param input the {@link Stream} to parse from. + * @return a {@link NewSessionTicket} object. + * @throws IOException + */ + public static NewSessionTicket Parse(Stream input) + { + long ticketLifetimeHint = TlsUtilities.ReadUint32(input); + byte[] ticket = TlsUtilities.ReadOpaque16(input); + return new NewSessionTicket(ticketLifetimeHint, ticket); + } + } +} diff --git a/crypto/src/crypto/tls/OcspStatusRequest.cs b/crypto/src/crypto/tls/OcspStatusRequest.cs new file mode 100644
index 000000000..2dd8371e5 --- /dev/null +++ b/crypto/src/crypto/tls/OcspStatusRequest.cs
@@ -0,0 +1,130 @@ +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.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 3546 3.6 + */ + public class OcspStatusRequest + { + protected readonly IList mResponderIDList; + protected readonly X509Extensions mRequestExtensions; + + /** + * @param responderIDList + * an {@link IList} of {@link ResponderID}, specifying the list of trusted OCSP + * responders. An empty list has the special meaning that the responders are + * implicitly known to the server - e.g., by prior arrangement. + * @param requestExtensions + * OCSP request extensions. A null value means that there are no extensions. + */ + public OcspStatusRequest(IList responderIDList, X509Extensions requestExtensions) + { + this.mResponderIDList = responderIDList; + this.mRequestExtensions = requestExtensions; + } + + /** + * @return an {@link IList} of {@link ResponderID} + */ + public virtual IList ResponderIDList + { + get { return mResponderIDList; } + } + + /** + * @return OCSP request extensions + */ + public virtual X509Extensions RequestExtensions + { + get { return mRequestExtensions; } + } + + /** + * Encode this {@link OcspStatusRequest} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + if (mResponderIDList == null || mResponderIDList.Count < 1) + { + TlsUtilities.WriteUint16(0, output); + } + else + { + MemoryStream buf = new MemoryStream(); + for (int i = 0; i < mResponderIDList.Count; ++i) + { + ResponderID responderID = (ResponderID)mResponderIDList[i]; + byte[] derEncoding = responderID.GetEncoded(Asn1Encodable.Der); + TlsUtilities.WriteOpaque16(derEncoding, buf); + } + TlsUtilities.CheckUint16(buf.Length); + TlsUtilities.WriteUint16((int)buf.Length, output); + buf.WriteTo(output); + } + + if (mRequestExtensions == null) + { + TlsUtilities.WriteUint16(0, output); + } + else + { + byte[] derEncoding = mRequestExtensions.GetEncoded(Asn1Encodable.Der); + TlsUtilities.CheckUint16(derEncoding.Length); + TlsUtilities.WriteUint16(derEncoding.Length, output); + output.Write(derEncoding, 0, derEncoding.Length); + } + } + + /** + * Parse a {@link OcspStatusRequest} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return an {@link OcspStatusRequest} object. + * @throws IOException + */ + public static OcspStatusRequest Parse(Stream input) + { + IList responderIDList = Platform.CreateArrayList(); + { + int length = TlsUtilities.ReadUint16(input); + if (length > 0) + { + byte[] data = TlsUtilities.ReadFully(length, input); + MemoryStream buf = new MemoryStream(data, false); + do + { + byte[] derEncoding = TlsUtilities.ReadOpaque16(buf); + ResponderID responderID = ResponderID.GetInstance(TlsUtilities.ReadDerObject(derEncoding)); + responderIDList.Add(responderID); + } + while (buf.Position < buf.Length); + } + } + + X509Extensions requestExtensions = null; + { + int length = TlsUtilities.ReadUint16(input); + if (length > 0) + { + byte[] derEncoding = TlsUtilities.ReadFully(length, input); + requestExtensions = X509Extensions.GetInstance(TlsUtilities.ReadDerObject(derEncoding)); + } + } + + return new OcspStatusRequest(responderIDList, requestExtensions); + } + } +} diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index e60688155..2ef80dcfd 100644 --- a/crypto/src/crypto/tls/PskTlsClient.cs +++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -3,200 +3,68 @@ using System.Collections; namespace Org.BouncyCastle.Crypto.Tls { - public abstract class PskTlsClient - :TlsClient + public class PskTlsClient + : AbstractTlsClient { - protected TlsCipherFactory cipherFactory; - protected TlsPskIdentity pskIdentity; - - protected TlsClientContext context; - - protected byte selectedCompressionMethod; - protected int selectedCipherSuite; + protected TlsPskIdentity mPskIdentity; public PskTlsClient(TlsPskIdentity pskIdentity) - : this(new DefaultTlsCipherFactory(), pskIdentity) + : this(new DefaultTlsCipherFactory(), pskIdentity) { } public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity) + : base(cipherFactory) { - this.cipherFactory = cipherFactory; - this.pskIdentity = pskIdentity; - } - - public virtual void Init(TlsClientContext context) - { - this.context = context; - } - - public virtual bool ShouldUseGmtUnixTime() - { - /* - * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that - * TLS implementors MUST by default set the entire value the ClientHello.Random and - * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random - * sequence. - */ - return false; - } - - public virtual int[] GetCipherSuites() - { - return new int[] { - CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA, - CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA, - CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_PSK_WITH_RC4_128_SHA, - }; - } - - public virtual IDictionary GetClientExtensions() - { - return null; - } - - public virtual byte[] GetCompressionMethods() - { - return new byte[] { CompressionMethod.NULL }; - } - - public virtual void NotifySessionID(byte[] sessionID) - { - // Currently ignored - } - - public virtual void NotifySelectedCipherSuite(int selectedCipherSuite) - { - this.selectedCipherSuite = selectedCipherSuite; + this.mPskIdentity = pskIdentity; } - public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod) + public override int[] GetCipherSuites() { - this.selectedCompressionMethod = selectedCompressionMethod; - } - - public virtual void NotifySecureRenegotiation(bool secureRenegotiation) - { - if (!secureRenegotiation) + return new int[] { - /* - * RFC 5746 3.4. If the extension is not present, the server does not support - * secure renegotiation; set secure_renegotiation flag to FALSE. In this case, - * some clients may want to terminate the handshake instead of continuing; see - * Section 4.1 for discussion. - */ -// throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - } - - public virtual void ProcessServerExtensions(IDictionary serverExtensions) - { + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA + }; } - public virtual TlsKeyExchange GetKeyExchange() + public override TlsKeyExchange GetKeyExchange() { - switch (selectedCipherSuite) - { - case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: - return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK); - - case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: - return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK); - - case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: - return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected cipher suite was in the list of client-offered cipher - * suites, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - } + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); - public abstract TlsAuthentication GetAuthentication(); - - public virtual TlsCompression GetCompression() - { - switch (selectedCompressionMethod) + switch (keyExchangeAlgorithm) { - case CompressionMethod.NULL: - return new TlsNullCompression(); + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.RSA_PSK: + return CreatePskKeyExchange(keyExchangeAlgorithm); - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected compression method was in the list of client-offered compression - * methods, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); } } - public virtual TlsCipher GetCipher() + public override TlsAuthentication GetAuthentication() { - switch (selectedCipherSuite) - { - case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, - DigestAlgorithm.SHA); - - case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, - DigestAlgorithm.SHA); - - case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, - DigestAlgorithm.SHA); - - case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: - case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, - DigestAlgorithm.SHA); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected cipher suite was in the list of client-offered cipher - * suites, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } + /* + * Note: This method is not called unless a server certificate is sent, which may be the + * case e.g. for RSA_PSK key exchange. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); } protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange) { - return new TlsPskKeyExchange(context, keyExchange, pskIdentity); + return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, null, mNamedCurves, + mClientECPointFormats, mServerECPointFormats); } } } diff --git a/crypto/src/crypto/tls/PskTlsServer.cs b/crypto/src/crypto/tls/PskTlsServer.cs new file mode 100644
index 000000000..85f3055fb --- /dev/null +++ b/crypto/src/crypto/tls/PskTlsServer.cs
@@ -0,0 +1,93 @@ +using System; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class PskTlsServer + : AbstractTlsServer + { + protected TlsPskIdentityManager mPskIdentityManager; + + public PskTlsServer(TlsPskIdentityManager pskIdentityManager) + : this(new DefaultTlsCipherFactory(), pskIdentityManager) + { + } + + public PskTlsServer(TlsCipherFactory cipherFactory, TlsPskIdentityManager pskIdentityManager) + : base(cipherFactory) + { + this.mPskIdentityManager = pskIdentityManager; + } + + protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual DHParameters GetDHParameters() + { + return DHStandardGroups.rfc5114_2048_256; + } + + protected override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA + }; + } + + public override TlsCredentials GetCredentials() + { + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); + + switch (keyExchangeAlgorithm) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + return null; + + case KeyExchangeAlgorithm.RSA_PSK: + return GetRsaEncryptionCredentials(); + + default: + /* Note: internal error here; selected a key exchange we don't implement! */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsKeyExchange GetKeyExchange() + { + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); + + switch (keyExchangeAlgorithm) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.RSA_PSK: + return CreatePskKeyExchange(keyExchangeAlgorithm); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange) + { + return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null, mPskIdentityManager, + GetDHParameters(), mNamedCurves, mClientECPointFormats, mServerECPointFormats); + } + } +} diff --git a/crypto/src/crypto/tls/RecordStream.cs b/crypto/src/crypto/tls/RecordStream.cs
index ce8882cbe..6f3fc41c6 100644 --- a/crypto/src/crypto/tls/RecordStream.cs +++ b/crypto/src/crypto/tls/RecordStream.cs
@@ -3,164 +3,336 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - /// <remarks>An implementation of the TLS 1.0 record layer.</remarks> + /// <summary>An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3.</summary> internal class RecordStream { - private TlsProtocolHandler handler; - private Stream inStr; - private Stream outStr; - private CombinedHash hash; - private TlsCompression readCompression = null; - private TlsCompression writeCompression = null; - private TlsCipher readCipher = null; - private TlsCipher writeCipher = null; - private MemoryStream buffer = new MemoryStream(); + private const int DEFAULT_PLAINTEXT_LIMIT = (1 << 14); - internal RecordStream( - TlsProtocolHandler handler, - Stream inStr, - Stream outStr) + internal const int TLS_HEADER_SIZE = 5; + internal const int TLS_HEADER_TYPE_OFFSET = 0; + internal const int TLS_HEADER_VERSION_OFFSET = 1; + internal const int TLS_HEADER_LENGTH_OFFSET = 3; + + private TlsProtocol mHandler; + private Stream mInput; + private Stream mOutput; + private TlsCompression mPendingCompression = null, mReadCompression = null, mWriteCompression = null; + private TlsCipher mPendingCipher = null, mReadCipher = null, mWriteCipher = null; + private long mReadSeqNo = 0, mWriteSeqNo = 0; + private MemoryStream mBuffer = new MemoryStream(); + + private TlsHandshakeHash mHandshakeHash = null; + + private ProtocolVersion mReadVersion = null, mWriteVersion = null; + private bool mRestrictReadVersion = true; + + private int mPlaintextLimit, mCompressedLimit, mCiphertextLimit; + + internal RecordStream(TlsProtocol handler, Stream input, Stream output) + { + this.mHandler = handler; + this.mInput = input; + this.mOutput = output; + this.mReadCompression = new TlsNullCompression(); + this.mWriteCompression = this.mReadCompression; + } + + internal virtual void Init(TlsContext context) + { + this.mReadCipher = new TlsNullCipher(context); + this.mWriteCipher = this.mReadCipher; + this.mHandshakeHash = new DeferredHash(); + this.mHandshakeHash.Init(context); + + SetPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT); + } + + internal virtual int GetPlaintextLimit() + { + return mPlaintextLimit; + } + + internal virtual void SetPlaintextLimit(int plaintextLimit) + { + this.mPlaintextLimit = plaintextLimit; + this.mCompressedLimit = this.mPlaintextLimit + 1024; + this.mCiphertextLimit = this.mCompressedLimit + 1024; + } + + internal virtual ProtocolVersion ReadVersion { - this.handler = handler; - this.inStr = inStr; - this.outStr = outStr; - this.hash = new CombinedHash(); - this.readCompression = new TlsNullCompression(); - this.writeCompression = this.readCompression; - this.readCipher = new TlsNullCipher(); - this.writeCipher = this.readCipher; + get { return mReadVersion; } + set { this.mReadVersion = value; } } - internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher) + internal virtual void SetWriteVersion(ProtocolVersion writeVersion) { - this.writeCompression = tlsCompression; - this.writeCipher = tlsCipher; + this.mWriteVersion = writeVersion; } - internal void ServerClientSpecReceived() + /** + * RFC 5246 E.1. "Earlier versions of the TLS specification were not fully clear on what the + * record layer version number (TLSPlaintext.version) should contain when sending ClientHello + * (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers + * compliant with this specification MUST accept any value {03,XX} as the record layer version + * number for ClientHello." + */ + internal virtual void SetRestrictReadVersion(bool enabled) { - this.readCompression = this.writeCompression; - this.readCipher = this.writeCipher; + this.mRestrictReadVersion = enabled; } - public void ReadData() + internal virtual void SetPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher) { - byte contentType = TlsUtilities.ReadUint8(inStr); - TlsUtilities.CheckVersion(inStr); - int size = TlsUtilities.ReadUint16(inStr); - byte[] buf = DecodeAndVerify(contentType, inStr, size); - handler.ProcessData(contentType, buf, 0, buf.Length); + this.mPendingCompression = tlsCompression; + this.mPendingCipher = tlsCipher; } - internal byte[] DecodeAndVerify( - byte contentType, - Stream inStr, - int len) + internal virtual void SentWriteCipherSpec() { - byte[] buf = new byte[len]; - TlsUtilities.ReadFully(buf, inStr); - byte[] decoded = readCipher.DecodeCiphertext(contentType, buf, 0, buf.Length); + if (mPendingCompression == null || mPendingCipher == null) + throw new TlsFatalAlert(AlertDescription.handshake_failure); - Stream cOut = readCompression.Decompress(buffer); + this.mWriteCompression = this.mPendingCompression; + this.mWriteCipher = this.mPendingCipher; + this.mWriteSeqNo = 0; + } + + internal virtual void ReceivedReadCipherSpec() + { + if (mPendingCompression == null || mPendingCipher == null) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + this.mReadCompression = this.mPendingCompression; + this.mReadCipher = this.mPendingCipher; + this.mReadSeqNo = 0; + } - if (cOut == buffer) + internal virtual void FinaliseHandshake() + { + if (mReadCompression != mPendingCompression || mWriteCompression != mPendingCompression + || mReadCipher != mPendingCipher || mWriteCipher != mPendingCipher) { - return decoded; + throw new TlsFatalAlert(AlertDescription.handshake_failure); } + this.mPendingCompression = null; + this.mPendingCipher = null; + } - cOut.Write(decoded, 0, decoded.Length); - cOut.Flush(); - byte[] contents = buffer.ToArray(); - buffer.SetLength(0); - return contents; + internal virtual bool ReadRecord() + { + byte[] recordHeader = TlsUtilities.ReadAllOrNothing(TLS_HEADER_SIZE, mInput); + if (recordHeader == null) + return false; + + byte type = TlsUtilities.ReadUint8(recordHeader, TLS_HEADER_TYPE_OFFSET); + + /* + * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an + * unexpected_message alert. + */ + CheckType(type, AlertDescription.unexpected_message); + + if (!mRestrictReadVersion) + { + int version = TlsUtilities.ReadVersionRaw(recordHeader, TLS_HEADER_VERSION_OFFSET); + if ((version & 0xffffff00) != 0x0300) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + else + { + ProtocolVersion version = TlsUtilities.ReadVersion(recordHeader, TLS_HEADER_VERSION_OFFSET); + if (mReadVersion == null) + { + mReadVersion = version; + } + else if (!version.Equals(mReadVersion)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + int length = TlsUtilities.ReadUint16(recordHeader, TLS_HEADER_LENGTH_OFFSET); + byte[] plaintext = DecodeAndVerify(type, mInput, length); + mHandler.ProcessRecord(type, plaintext, 0, plaintext.Length); + return true; } - internal void WriteMessage( - byte type, - byte[] message, - int offset, - int len) + internal virtual byte[] DecodeAndVerify(byte type, Stream input, int len) { + CheckLength(len, mCiphertextLimit, AlertDescription.record_overflow); + + byte[] buf = TlsUtilities.ReadFully(len, input); + byte[] decoded = mReadCipher.DecodeCiphertext(mReadSeqNo++, type, buf, 0, buf.Length); + + CheckLength(decoded.Length, mCompressedLimit, AlertDescription.record_overflow); + + /* + * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for + * ensuring that messages cannot cause internal buffer overflows. + */ + Stream cOut = mReadCompression.Decompress(mBuffer); + if (cOut != mBuffer) + { + cOut.Write(decoded, 0, decoded.Length); + cOut.Flush(); + decoded = GetBufferContents(); + } + + /* + * RFC 5264 6.2.2. If the decompression function encounters a TLSCompressed.fragment that + * would decompress to a length in excess of 2^14 bytes, it should report a fatal + * decompression failure error. + */ + CheckLength(decoded.Length, mPlaintextLimit, AlertDescription.decompression_failure); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (decoded.Length < 1 && type != ContentType.application_data) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return decoded; + } + + internal virtual void WriteRecord(byte type, byte[] plaintext, int plaintextOffset, int plaintextLength) + { + // Never send anything until a valid ClientHello has been received + if (mWriteVersion == null) + return; + + /* + * RFC 5264 6. Implementations MUST NOT send record types not defined in this document + * unless negotiated by some extension. + */ + CheckType(type, AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 The length should not exceed 2^14. + */ + CheckLength(plaintextLength, mPlaintextLimit, AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (plaintextLength < 1 && type != ContentType.application_data) + throw new TlsFatalAlert(AlertDescription.internal_error); + if (type == ContentType.handshake) { - UpdateHandshakeData(message, offset, len); + UpdateHandshakeData(plaintext, plaintextOffset, plaintextLength); } - Stream cOut = writeCompression.Compress(buffer); + Stream cOut = mWriteCompression.Compress(mBuffer); byte[] ciphertext; - if (cOut == buffer) + if (cOut == mBuffer) { - ciphertext = writeCipher.EncodePlaintext(type, message, offset, len); + ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, plaintext, plaintextOffset, plaintextLength); } else { - cOut.Write(message, offset, len); + cOut.Write(plaintext, plaintextOffset, plaintextLength); cOut.Flush(); - ciphertext = writeCipher.EncodePlaintext(type, buffer.GetBuffer(), 0, (int)buffer.Position); - buffer.SetLength(0); + byte[] compressed = GetBufferContents(); + + /* + * RFC5264 6.2.2. Compression must be lossless and may not increase the content length + * by more than 1024 bytes. + */ + CheckLength(compressed.Length, plaintextLength + 1024, AlertDescription.internal_error); + + ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, compressed, 0, compressed.Length); } - byte[] writeMessage = new byte[ciphertext.Length + 5]; - TlsUtilities.WriteUint8((byte)type, writeMessage, 0); - TlsUtilities.WriteVersion(writeMessage, 1); - TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3); - Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length); - outStr.Write(writeMessage, 0, writeMessage.Length); - outStr.Flush(); + /* + * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048. + */ + CheckLength(ciphertext.Length, mCiphertextLimit, AlertDescription.internal_error); + + byte[] record = new byte[ciphertext.Length + TLS_HEADER_SIZE]; + TlsUtilities.WriteUint8(type, record, TLS_HEADER_TYPE_OFFSET); + TlsUtilities.WriteVersion(mWriteVersion, record, TLS_HEADER_VERSION_OFFSET); + TlsUtilities.WriteUint16(ciphertext.Length, record, TLS_HEADER_LENGTH_OFFSET); + Array.Copy(ciphertext, 0, record, TLS_HEADER_SIZE, ciphertext.Length); + mOutput.Write(record, 0, record.Length); + mOutput.Flush(); } - internal void UpdateHandshakeData( - byte[] message, - int offset, - int len) + internal virtual void NotifyHelloComplete() { - hash.BlockUpdate(message, offset, len); + this.mHandshakeHash = mHandshakeHash.NotifyPrfDetermined(); } - internal byte[] GetCurrentHash() + internal virtual TlsHandshakeHash HandshakeHash { - return DoFinal(new CombinedHash(hash)); + get { return mHandshakeHash; } } - internal void Close() + internal virtual TlsHandshakeHash PrepareToFinish() + { + TlsHandshakeHash result = mHandshakeHash; + this.mHandshakeHash = mHandshakeHash.StopTracking(); + return result; + } + + internal virtual void UpdateHandshakeData(byte[] message, int offset, int len) + { + mHandshakeHash.BlockUpdate(message, offset, len); + } + + internal virtual void SafeClose() { - IOException e = null; try { - inStr.Close(); + mInput.Close(); } - catch (IOException ex) + catch (IOException) { - e = ex; } try { - // NB: This is harmless if outStr == inStr - outStr.Close(); + mOutput.Close(); } - catch (IOException ex) + catch (IOException) { - e = ex; } + } - if (e != null) - { - throw e; - } + internal virtual void Flush() + { + mOutput.Flush(); + } + + private byte[] GetBufferContents() + { + byte[] contents = mBuffer.ToArray(); + mBuffer.SetLength(0); + return contents; } - internal void Flush() + private static void CheckType(byte type, byte alertDescription) { - outStr.Flush(); + switch (type) + { + case ContentType.application_data: + case ContentType.alert: + case ContentType.change_cipher_spec: + case ContentType.handshake: + case ContentType.heartbeat: + break; + default: + throw new TlsFatalAlert(alertDescription); + } } - private static byte[] DoFinal(CombinedHash ch) + private static void CheckLength(int length, int limit, byte alertDescription) { - byte[] bs = new byte[ch.GetDigestSize()]; - ch.DoFinal(bs, 0); - return bs; + if (length > limit) + throw new TlsFatalAlert(alertDescription); } } } diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 9ed3969eb..3b851587d 100644 --- a/crypto/src/crypto/tls/SecurityParameters.cs +++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -1,26 +1,103 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Tls { - public class SecurityParameters - { - internal byte[] clientRandom = null; - internal byte[] serverRandom = null; - internal byte[] masterSecret = null; - - public byte[] ClientRandom - { - get { return clientRandom; } - } - - public byte[] ServerRandom - { - get { return serverRandom; } - } - - public byte[] MasterSecret - { - get { return masterSecret; } - } - } + public class SecurityParameters + { + internal int entity = -1; + internal int cipherSuite = -1; + internal byte compressionAlgorithm = CompressionMethod.cls_null; + internal int prfAlgorithm = -1; + internal int verifyDataLength = -1; + internal byte[] masterSecret = null; + internal byte[] clientRandom = null; + internal byte[] serverRandom = null; + internal byte[] sessionHash = null; + internal byte[] pskIdentity = null; + internal byte[] srpIdentity = null; + + // TODO Keep these internal, since it's maybe not the ideal place for them + internal short maxFragmentLength = -1; + internal bool truncatedHMac = false; + internal bool encryptThenMac = false; + internal bool extendedMasterSecret = false; + + internal virtual void Clear() + { + if (this.masterSecret != null) + { + Arrays.Fill(this.masterSecret, (byte)0); + this.masterSecret = null; + } + } + + /** + * @return {@link ConnectionEnd} + */ + public virtual int Entity + { + get { return entity; } + } + + /** + * @return {@link CipherSuite} + */ + public virtual int CipherSuite + { + get { return cipherSuite; } + } + + /** + * @return {@link CompressionMethod} + */ + public byte CompressionAlgorithm + { + get { return compressionAlgorithm; } + } + + /** + * @return {@link PRFAlgorithm} + */ + public virtual int PrfAlgorithm + { + get { return prfAlgorithm; } + } + + public virtual int VerifyDataLength + { + get { return verifyDataLength; } + } + + public virtual byte[] MasterSecret + { + get { return masterSecret; } + } + + public virtual byte[] ClientRandom + { + get { return clientRandom; } + } + + public virtual byte[] ServerRandom + { + get { return serverRandom; } + } + + public virtual byte[] SessionHash + { + get { return sessionHash; } + } + + public virtual byte[] PskIdentity + { + get { return pskIdentity; } + } + + public virtual byte[] SrpIdentity + { + get { return srpIdentity; } + } + } } diff --git a/crypto/src/crypto/tls/ServerDHParams.cs b/crypto/src/crypto/tls/ServerDHParams.cs new file mode 100644
index 000000000..b09262771 --- /dev/null +++ b/crypto/src/crypto/tls/ServerDHParams.cs
@@ -0,0 +1,61 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerDHParams + { + protected readonly DHPublicKeyParameters mPublicKey; + + public ServerDHParams(DHPublicKeyParameters publicKey) + { + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + + this.mPublicKey = publicKey; + } + + public virtual DHPublicKeyParameters PublicKey + { + get { return mPublicKey; } + } + + /** + * Encode this {@link ServerDHParams} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + DHParameters dhParameters = mPublicKey.Parameters; + BigInteger Ys = mPublicKey.Y; + + TlsDHUtilities.WriteDHParameter(dhParameters.P, output); + TlsDHUtilities.WriteDHParameter(dhParameters.G, output); + TlsDHUtilities.WriteDHParameter(Ys, output); + } + + /** + * Parse a {@link ServerDHParams} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerDHParams} object. + * @throws IOException + */ + public static ServerDHParams Parse(Stream input) + { + BigInteger p = TlsDHUtilities.ReadDHParameter(input); + BigInteger g = TlsDHUtilities.ReadDHParameter(input); + BigInteger Ys = TlsDHUtilities.ReadDHParameter(input); + + return new ServerDHParams( + TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g)))); + } + } +} diff --git a/crypto/src/crypto/tls/ServerName.cs b/crypto/src/crypto/tls/ServerName.cs new file mode 100644
index 000000000..3d1e8f844 --- /dev/null +++ b/crypto/src/crypto/tls/ServerName.cs
@@ -0,0 +1,105 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerName + { + protected readonly byte mNameType; + protected readonly object mName; + + public ServerName(byte nameType, object name) + { + if (!IsCorrectType(nameType, name)) + throw new ArgumentException("not an instance of the correct type", "name"); + + this.mNameType = nameType; + this.mName = name; + } + + public virtual byte NameType + { + get { return mNameType; } + } + + public virtual object Name + { + get { return mName; } + } + + public virtual string GetHostName() + { + if (!IsCorrectType(Tls.NameType.host_name, mName)) + throw new InvalidOperationException("'name' is not a HostName string"); + + return (string)mName; + } + + /** + * Encode this {@link ServerName} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mNameType, output); + + switch (mNameType) + { + case Tls.NameType.host_name: + byte[] utf8Encoding = Strings.ToUtf8ByteArray((string)mName); + if (utf8Encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.internal_error); + TlsUtilities.WriteOpaque16(utf8Encoding, output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link ServerName} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerName} object. + * @throws IOException + */ + public static ServerName Parse(Stream input) + { + byte name_type = TlsUtilities.ReadUint8(input); + object name; + + switch (name_type) + { + case Tls.NameType.host_name: + { + byte[] utf8Encoding = TlsUtilities.ReadOpaque16(input); + if (utf8Encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + name = Strings.FromUtf8ByteArray(utf8Encoding); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new ServerName(name_type, name); + } + + protected static bool IsCorrectType(byte nameType, object name) + { + switch (nameType) + { + case Tls.NameType.host_name: + return name is string; + default: + throw new ArgumentException("unsupported value", "name"); + } + } + } +} diff --git a/crypto/src/crypto/tls/ServerNameList.cs b/crypto/src/crypto/tls/ServerNameList.cs new file mode 100644
index 000000000..13da79bf6 --- /dev/null +++ b/crypto/src/crypto/tls/ServerNameList.cs
@@ -0,0 +1,81 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerNameList + { + protected readonly IList mServerNameList; + + /** + * @param serverNameList an {@link IList} of {@link ServerName}. + */ + public ServerNameList(IList serverNameList) + { + if (serverNameList == null || serverNameList.Count < 1) + throw new ArgumentException("must not be null or empty", "serverNameList"); + + this.mServerNameList = serverNameList; + } + + /** + * @return an {@link IList} of {@link ServerName}. + */ + public virtual IList ServerNames + { + get { return mServerNameList; } + } + + /** + * Encode this {@link ServerNameList} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + MemoryStream buf = new MemoryStream(); + + foreach (ServerName entry in ServerNames) + { + entry.Encode(buf); + } + + TlsUtilities.CheckUint16(buf.Length); + TlsUtilities.WriteUint16((int)buf.Length, output); + buf.WriteTo(output); + } + + /** + * Parse a {@link ServerNameList} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerNameList} object. + * @throws IOException + */ + public static ServerNameList Parse(Stream input) + { + int length = TlsUtilities.ReadUint16(input); + if (length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] data = TlsUtilities.ReadFully(length, input); + + MemoryStream buf = new MemoryStream(data, false); + + IList server_name_list = Platform.CreateArrayList(); + while (buf.Position < buf.Length) + { + ServerName entry = ServerName.Parse(buf); + server_name_list.Add(entry); + } + + return new ServerNameList(server_name_list); + } + } +} diff --git a/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs new file mode 100644
index 000000000..485889709 --- /dev/null +++ b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class ServerOnlyTlsAuthentication + : TlsAuthentication + { + public abstract void NotifyServerCertificate(Certificate serverCertificate); + + public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + return null; + } + } +} diff --git a/crypto/src/crypto/tls/ServerSrpParams.cs b/crypto/src/crypto/tls/ServerSrpParams.cs new file mode 100644
index 000000000..556ac5310 --- /dev/null +++ b/crypto/src/crypto/tls/ServerSrpParams.cs
@@ -0,0 +1,75 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerSrpParams + { + protected BigInteger m_N, m_g, m_B; + protected byte[] m_s; + + public ServerSrpParams(BigInteger N, BigInteger g, byte[] s, BigInteger B) + { + this.m_N = N; + this.m_g = g; + this.m_s = Arrays.Clone(s); + this.m_B = B; + } + + public virtual BigInteger B + { + get { return m_B; } + } + + public virtual BigInteger G + { + get { return m_g; } + } + + public virtual BigInteger N + { + get { return m_N; } + } + + public virtual byte[] S + { + get { return m_s; } + } + + /** + * Encode this {@link ServerSRPParams} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsSrpUtilities.WriteSrpParameter(m_N, output); + TlsSrpUtilities.WriteSrpParameter(m_g, output); + TlsUtilities.WriteOpaque8(m_s, output); + TlsSrpUtilities.WriteSrpParameter(m_B, output); + } + + /** + * Parse a {@link ServerSRPParams} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link ServerSRPParams} object. + * @throws IOException + */ + public static ServerSrpParams Parse(Stream input) + { + BigInteger N = TlsSrpUtilities.ReadSrpParameter(input); + BigInteger g = TlsSrpUtilities.ReadSrpParameter(input); + byte[] s = TlsUtilities.ReadOpaque8(input); + BigInteger B = TlsSrpUtilities.ReadSrpParameter(input); + + return new ServerSrpParams(N, g, s, B); + } + } +} diff --git a/crypto/src/crypto/tls/SessionParameters.cs b/crypto/src/crypto/tls/SessionParameters.cs new file mode 100644
index 000000000..a1eb5f27c --- /dev/null +++ b/crypto/src/crypto/tls/SessionParameters.cs
@@ -0,0 +1,165 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public sealed class SessionParameters + { + public sealed class Builder + { + private int mCipherSuite = -1; + private short mCompressionAlgorithm = -1; + private byte[] mMasterSecret = null; + private Certificate mPeerCertificate = null; + private byte[] mPskIdentity = null; + private byte[] mSrpIdentity = null; + private byte[] mEncodedServerExtensions = null; + + public Builder() + { + } + + public SessionParameters Build() + { + Validate(this.mCipherSuite >= 0, "cipherSuite"); + Validate(this.mCompressionAlgorithm >= 0, "compressionAlgorithm"); + Validate(this.mMasterSecret != null, "masterSecret"); + return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate, + mPskIdentity, mSrpIdentity, mEncodedServerExtensions); + } + + public Builder SetCipherSuite(int cipherSuite) + { + this.mCipherSuite = cipherSuite; + return this; + } + + public Builder SetCompressionAlgorithm(byte compressionAlgorithm) + { + this.mCompressionAlgorithm = compressionAlgorithm; + return this; + } + + public Builder SetMasterSecret(byte[] masterSecret) + { + this.mMasterSecret = masterSecret; + return this; + } + + public Builder SetPeerCertificate(Certificate peerCertificate) + { + this.mPeerCertificate = peerCertificate; + return this; + } + + public Builder SetPskIdentity(byte[] pskIdentity) + { + this.mPskIdentity = pskIdentity; + return this; + } + + public Builder SetSrpIdentity(byte[] srpIdentity) + { + this.mSrpIdentity = srpIdentity; + return this; + } + + public Builder SetServerExtensions(IDictionary serverExtensions) + { + if (serverExtensions == null) + { + mEncodedServerExtensions = null; + } + else + { + MemoryStream buf = new MemoryStream(); + TlsProtocol.WriteExtensions(buf, serverExtensions); + mEncodedServerExtensions = buf.ToArray(); + } + return this; + } + + private void Validate(bool condition, string parameter) + { + if (!condition) + throw new InvalidOperationException("Required session parameter '" + parameter + "' not configured"); + } + } + + private int mCipherSuite; + private byte mCompressionAlgorithm; + private byte[] mMasterSecret; + private Certificate mPeerCertificate; + private byte[] mPskIdentity; + private byte[] mSrpIdentity; + private byte[] mEncodedServerExtensions; + + private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret, + Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions) + { + this.mCipherSuite = cipherSuite; + this.mCompressionAlgorithm = compressionAlgorithm; + this.mMasterSecret = Arrays.Clone(masterSecret); + this.mPeerCertificate = peerCertificate; + this.mPskIdentity = Arrays.Clone(pskIdentity); + this.mSrpIdentity = Arrays.Clone(srpIdentity); + this.mEncodedServerExtensions = encodedServerExtensions; + } + + public void Clear() + { + if (this.mMasterSecret != null) + { + Arrays.Fill(this.mMasterSecret, (byte)0); + } + } + + public SessionParameters Copy() + { + return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate, + mPskIdentity, mSrpIdentity, mEncodedServerExtensions); + } + + public int CipherSuite + { + get { return mCipherSuite; } + } + + public byte CompressionAlgorithm + { + get { return mCompressionAlgorithm; } + } + + public byte[] MasterSecret + { + get { return mMasterSecret; } + } + + public Certificate PeerCertificate + { + get { return mPeerCertificate; } + } + + public byte[] PskIdentity + { + get { return mPskIdentity; } + } + + public byte[] SrpIdentity + { + get { return mSrpIdentity; } + } + + public IDictionary ReadServerExtensions() + { + if (mEncodedServerExtensions == null) + return null; + + MemoryStream buf = new MemoryStream(mEncodedServerExtensions, false); + return TlsProtocol.ReadExtensions(buf); + } + } +} diff --git a/crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs b/crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs new file mode 100644
index 000000000..3e9737cd7 --- /dev/null +++ b/crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs
@@ -0,0 +1,69 @@ +using System; + +using Org.BouncyCastle.Crypto.Agreement.Srp; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * An implementation of {@link TlsSRPIdentityManager} that simulates the existence of "unknown" identities + * to obscure the fact that there is no verifier for them. + */ + public class SimulatedTlsSrpIdentityManager + : TlsSrpIdentityManager + { + private static readonly byte[] PREFIX_PASSWORD = Strings.ToByteArray("password"); + private static readonly byte[] PREFIX_SALT = Strings.ToByteArray("salt"); + + /** + * Create a {@link SimulatedTlsSRPIdentityManager} that implements the algorithm from RFC 5054 2.5.1.3 + * + * @param group the {@link SRP6GroupParameters} defining the group that SRP is operating in + * @param seedKey the secret "seed key" referred to in RFC 5054 2.5.1.3 + * @return an instance of {@link SimulatedTlsSRPIdentityManager} + */ + public static SimulatedTlsSrpIdentityManager GetRfc5054Default(Srp6GroupParameters group, byte[] seedKey) + { + Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator(); + verifierGenerator.Init(group, TlsUtilities.CreateHash(HashAlgorithm.sha1)); + + HMac mac = new HMac(TlsUtilities.CreateHash(HashAlgorithm.sha1)); + mac.Init(new KeyParameter(seedKey)); + + return new SimulatedTlsSrpIdentityManager(group, verifierGenerator, mac); + } + + protected readonly Srp6GroupParameters mGroup; + protected readonly Srp6VerifierGenerator mVerifierGenerator; + protected readonly IMac mMac; + + public SimulatedTlsSrpIdentityManager(Srp6GroupParameters group, Srp6VerifierGenerator verifierGenerator, IMac mac) + { + this.mGroup = group; + this.mVerifierGenerator = verifierGenerator; + this.mMac = mac; + } + + public virtual TlsSrpLoginParameters GetLoginParameters(byte[] identity) + { + mMac.BlockUpdate(PREFIX_SALT, 0, PREFIX_SALT.Length); + mMac.BlockUpdate(identity, 0, identity.Length); + + byte[] salt = new byte[mMac.GetMacSize()]; + mMac.DoFinal(salt, 0); + + mMac.BlockUpdate(PREFIX_PASSWORD, 0, PREFIX_PASSWORD.Length); + mMac.BlockUpdate(identity, 0, identity.Length); + + byte[] password = new byte[mMac.GetMacSize()]; + mMac.DoFinal(password, 0); + + BigInteger verifier = mVerifierGenerator.GenerateVerifier(salt, identity, password); + + return new TlsSrpLoginParameters(mGroup, verifier, salt); + } + } +} diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index 3769fc85d..df1607751 100644 --- a/crypto/src/crypto/tls/SrpTlsClient.cs +++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -6,194 +6,99 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public abstract class SrpTlsClient - : TlsClient + public class SrpTlsClient + : AbstractTlsClient { - protected TlsCipherFactory cipherFactory; - protected byte[] identity; - protected byte[] password; + protected TlsSrpGroupVerifier mGroupVerifier; - protected TlsClientContext context; - - protected byte selectedCompressionMethod; - protected int selectedCipherSuite; + protected byte[] mIdentity; + protected byte[] mPassword; public SrpTlsClient(byte[] identity, byte[] password) - : this(new DefaultTlsCipherFactory(), identity, password) + : this(new DefaultTlsCipherFactory(), new DefaultTlsSrpGroupVerifier(), identity, password) { } public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password) + : this(cipherFactory, new DefaultTlsSrpGroupVerifier(), identity, password) { - this.cipherFactory = cipherFactory; - this.identity = Arrays.Clone(identity); - this.password = Arrays.Clone(password); } - public virtual void Init(TlsClientContext context) + public SrpTlsClient(TlsCipherFactory cipherFactory, TlsSrpGroupVerifier groupVerifier, + byte[] identity, byte[] password) + : base(cipherFactory) { - this.context = context; + this.mGroupVerifier = groupVerifier; + this.mIdentity = Arrays.Clone(identity); + this.mPassword = Arrays.Clone(password); } - public virtual bool ShouldUseGmtUnixTime() + protected virtual bool RequireSrpServerExtension { - /* - * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that - * TLS implementors MUST by default set the entire value the ClientHello.Random and - * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random - * sequence. - */ - return false; + // No explicit guidance in RFC 5054; by default an (empty) extension from server is optional + get { return false; } } - public virtual int[] GetCipherSuites() + public override int[] GetCipherSuites() { - return new int[] { - CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, + return new int[] + { + CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA }; } - public virtual IDictionary GetClientExtensions() + public override IDictionary GetClientExtensions() { - IDictionary clientExtensions = Platform.CreateHashtable(); - - MemoryStream srpData = new MemoryStream(); - TlsUtilities.WriteOpaque8(this.identity, srpData); - clientExtensions[ExtensionType.srp] = srpData.ToArray(); - + IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions()); + TlsSrpUtilities.AddSrpExtension(clientExtensions, this.mIdentity); return clientExtensions; } - public virtual byte[] GetCompressionMethods() - { - return new byte[] { CompressionMethod.NULL }; - } - - public virtual void NotifySessionID(byte[] sessionID) + public override void ProcessServerExtensions(IDictionary serverExtensions) { - // Currently ignored - } - - public virtual void NotifySelectedCipherSuite(int selectedCipherSuite) - { - this.selectedCipherSuite = selectedCipherSuite; - } - - public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod) - { - this.selectedCompressionMethod = selectedCompressionMethod; - } - - public virtual void NotifySecureRenegotiation(bool secureRenegotiation) - { - if (!secureRenegotiation) + if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp, + AlertDescription.illegal_parameter)) { - /* - * RFC 5746 3.4. If the extension is not present, the server does not support - * secure renegotiation; set secure_renegotiation flag to FALSE. In this case, - * some clients may want to terminate the handshake instead of continuing; see - * Section 4.1 for discussion. - */ -// throw new TlsFatalAlert(AlertDescription.handshake_failure); + if (RequireSrpServerExtension) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - } - public virtual void ProcessServerExtensions(IDictionary serverExtensions) - { - // There is no server response for the SRP extension + base.ProcessServerExtensions(serverExtensions); } - public virtual TlsKeyExchange GetKeyExchange() + public override TlsKeyExchange GetKeyExchange() { - switch (selectedCipherSuite) - { - case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: - return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP); - - case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: - return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA); - - case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: - return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected cipher suite was in the list of client-offered cipher - * suites, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - } - - public abstract TlsAuthentication GetAuthentication(); + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); - public virtual TlsCompression GetCompression() - { - switch (selectedCompressionMethod) + switch (keyExchangeAlgorithm) { - case CompressionMethod.NULL: - return new TlsNullCompression(); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected compression method was in the list of client-offered compression - * methods, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); + case KeyExchangeAlgorithm.SRP: + case KeyExchangeAlgorithm.SRP_DSS: + case KeyExchangeAlgorithm.SRP_RSA: + return CreateSrpKeyExchange(keyExchangeAlgorithm); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); } } - public virtual TlsCipher GetCipher() + public override TlsAuthentication GetAuthentication() { - switch (selectedCipherSuite) - { - case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA); - - case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA); - - case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: - case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: - return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA); - - default: - /* - * Note: internal error here; the TlsProtocolHandler verifies that the - * server-selected cipher suite was in the list of client-offered cipher - * suites, so if we now can't produce an implementation, we shouldn't have - * offered it! - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } + /* + * Note: This method is not called unless a server certificate is sent, which may be the + * case e.g. for SRP_DSS or SRP_RSA key exchange. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); } protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange) { - return new TlsSrpKeyExchange(context, keyExchange, identity, password); + return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mGroupVerifier, mIdentity, mPassword); } } } diff --git a/crypto/src/crypto/tls/SrpTlsServer.cs b/crypto/src/crypto/tls/SrpTlsServer.cs new file mode 100644
index 000000000..f97878380 --- /dev/null +++ b/crypto/src/crypto/tls/SrpTlsServer.cs
@@ -0,0 +1,121 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class SrpTlsServer + : AbstractTlsServer + { + protected TlsSrpIdentityManager mSrpIdentityManager; + + protected byte[] mSrpIdentity = null; + protected TlsSrpLoginParameters mLoginParameters = null; + + public SrpTlsServer(TlsSrpIdentityManager srpIdentityManager) + : this(new DefaultTlsCipherFactory(), srpIdentityManager) + { + } + + public SrpTlsServer(TlsCipherFactory cipherFactory, TlsSrpIdentityManager srpIdentityManager) + : base(cipherFactory) + { + this.mSrpIdentityManager = srpIdentityManager; + } + + protected virtual TlsSignerCredentials GetDsaSignerCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual TlsSignerCredentials GetRsaSignerCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA + }; + } + + public override void ProcessClientExtensions(IDictionary clientExtensions) + { + base.ProcessClientExtensions(clientExtensions); + + this.mSrpIdentity = TlsSrpUtilities.GetSrpExtension(clientExtensions); + } + + public override int GetSelectedCipherSuite() + { + int cipherSuite = base.GetSelectedCipherSuite(); + + if (TlsSrpUtilities.IsSrpCipherSuite(cipherSuite)) + { + if (mSrpIdentity != null) + { + this.mLoginParameters = mSrpIdentityManager.GetLoginParameters(mSrpIdentity); + } + + if (mLoginParameters == null) + throw new TlsFatalAlert(AlertDescription.unknown_psk_identity); + } + + return cipherSuite; + } + + public override TlsCredentials GetCredentials() + { + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); + + switch (keyExchangeAlgorithm) + { + case KeyExchangeAlgorithm.SRP: + return null; + + case KeyExchangeAlgorithm.SRP_DSS: + return GetDsaSignerCredentials(); + + case KeyExchangeAlgorithm.SRP_RSA: + return GetRsaSignerCredentials(); + + default: + /* Note: internal error here; selected a key exchange we don't implement! */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsKeyExchange GetKeyExchange() + { + int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite); + + switch (keyExchangeAlgorithm) + { + case KeyExchangeAlgorithm.SRP: + case KeyExchangeAlgorithm.SRP_DSS: + case KeyExchangeAlgorithm.SRP_RSA: + return CreateSrpKeyExchange(keyExchangeAlgorithm); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange) + { + return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mSrpIdentity, mLoginParameters); + } + } +} diff --git a/crypto/src/crypto/tls/Ssl3Mac.cs b/crypto/src/crypto/tls/Ssl3Mac.cs
index b2f3f309e..8bdb342dc 100644 --- a/crypto/src/crypto/tls/Ssl3Mac.cs +++ b/crypto/src/crypto/tls/Ssl3Mac.cs
@@ -6,109 +6,105 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - /** - * HMAC implementation based on original internet draft for HMAC (RFC 2104) - * - * The difference is that padding is concatentated versus XORed with the key - * - * H(K + opad, H(K + ipad, text)) - */ - public class Ssl3Mac - : IMac - { - private const byte IPAD = 0x36; - private const byte OPAD = 0x5C; - - internal static readonly byte[] MD5_IPAD = GenPad(IPAD, 48); - internal static readonly byte[] MD5_OPAD = GenPad(OPAD, 48); - internal static readonly byte[] SHA1_IPAD = GenPad(IPAD, 40); - internal static readonly byte[] SHA1_OPAD = GenPad(OPAD, 40); - - private IDigest digest; - - private byte[] secret; - private byte[] ipad, opad; - - /** - * Base constructor for one of the standard digest algorithms that the byteLength of - * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1. - * - * @param digest the digest. - */ - public Ssl3Mac(IDigest digest) - { - this.digest = digest; - - if (digest.GetDigestSize() == 20) - { - this.ipad = SHA1_IPAD; - this.opad = SHA1_OPAD; - } - else - { - this.ipad = MD5_IPAD; - this.opad = MD5_OPAD; - } - } - - public virtual string AlgorithmName - { - get { return digest.AlgorithmName + "/SSL3MAC"; } - } - - public virtual void Init(ICipherParameters parameters) - { - secret = Arrays.Clone(((KeyParameter)parameters).GetKey()); - - Reset(); - } - - public virtual int GetMacSize() - { - return digest.GetDigestSize(); - } - - public virtual void Update(byte input) - { - digest.Update(input); - } - - public virtual void BlockUpdate(byte[] input, int inOff, int len) - { - digest.BlockUpdate(input, inOff, len); - } - - public virtual int DoFinal(byte[] output, int outOff) - { - byte[] tmp = new byte[digest.GetDigestSize()]; - digest.DoFinal(tmp, 0); - - digest.BlockUpdate(secret, 0, secret.Length); - digest.BlockUpdate(opad, 0, opad.Length); - digest.BlockUpdate(tmp, 0, tmp.Length); - - int len = digest.DoFinal(output, outOff); - - Reset(); - - return len; - } - - /** - * Reset the mac generator. - */ - public virtual void Reset() - { - digest.Reset(); - digest.BlockUpdate(secret, 0, secret.Length); - digest.BlockUpdate(ipad, 0, ipad.Length); - } - - private static byte[] GenPad(byte b, int count) - { - byte[] padding = new byte[count]; - Arrays.Fill(padding, b); - return padding; - } - } + /** + * HMAC implementation based on original internet draft for HMAC (RFC 2104) + * + * The difference is that padding is concatentated versus XORed with the key + * + * H(K + opad, H(K + ipad, text)) + */ + public class Ssl3Mac + : IMac + { + private const byte IPAD_BYTE = 0x36; + private const byte OPAD_BYTE = 0x5C; + + internal static readonly byte[] IPAD = GenPad(IPAD_BYTE, 48); + internal static readonly byte[] OPAD = GenPad(OPAD_BYTE, 48); + + private readonly IDigest digest; + private readonly int padLength; + + private byte[] secret; + + /** + * Base constructor for one of the standard digest algorithms that the byteLength of + * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1. + * + * @param digest the digest. + */ + public Ssl3Mac(IDigest digest) + { + this.digest = digest; + + if (digest.GetDigestSize() == 20) + { + this.padLength = 40; + } + else + { + this.padLength = 48; + } + } + + public virtual string AlgorithmName + { + get { return digest.AlgorithmName + "/SSL3MAC"; } + } + + public virtual void Init(ICipherParameters parameters) + { + secret = Arrays.Clone(((KeyParameter)parameters).GetKey()); + + Reset(); + } + + public virtual int GetMacSize() + { + return digest.GetDigestSize(); + } + + public virtual void Update(byte input) + { + digest.Update(input); + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + digest.BlockUpdate(input, inOff, len); + } + + public virtual int DoFinal(byte[] output, int outOff) + { + byte[] tmp = new byte[digest.GetDigestSize()]; + digest.DoFinal(tmp, 0); + + digest.BlockUpdate(secret, 0, secret.Length); + digest.BlockUpdate(OPAD, 0, padLength); + digest.BlockUpdate(tmp, 0, tmp.Length); + + int len = digest.DoFinal(output, outOff); + + Reset(); + + return len; + } + + /** + * Reset the mac generator. + */ + public virtual void Reset() + { + digest.Reset(); + digest.BlockUpdate(secret, 0, secret.Length); + digest.BlockUpdate(IPAD, 0, padLength); + } + + private static byte[] GenPad(byte b, int count) + { + byte[] padding = new byte[count]; + Arrays.Fill(padding, b); + return padding; + } + } } diff --git a/crypto/src/crypto/tls/SupplementalDataEntry.cs b/crypto/src/crypto/tls/SupplementalDataEntry.cs new file mode 100644
index 000000000..5adc4fa52 --- /dev/null +++ b/crypto/src/crypto/tls/SupplementalDataEntry.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class SupplementalDataEntry + { + protected readonly int mDataType; + protected readonly byte[] mData; + + public SupplementalDataEntry(int dataType, byte[] data) + { + this.mDataType = dataType; + this.mData = data; + } + + public virtual int DataType + { + get { return mDataType; } + } + + public virtual byte[] Data + { + get { return mData; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsAeadCipher.cs b/crypto/src/crypto/tls/TlsAeadCipher.cs new file mode 100644
index 000000000..951e8663b --- /dev/null +++ b/crypto/src/crypto/tls/TlsAeadCipher.cs
@@ -0,0 +1,189 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsAeadCipher + : TlsCipher + { + protected readonly TlsContext context; + protected readonly int macSize; + protected readonly int nonce_explicit_length; + + protected readonly IAeadBlockCipher encryptCipher; + protected readonly IAeadBlockCipher decryptCipher; + + protected readonly byte[] encryptImplicitNonce, decryptImplicitNonce; + + /// <exception cref="IOException"></exception> + public TlsAeadCipher(TlsContext context, IAeadBlockCipher clientWriteCipher, IAeadBlockCipher serverWriteCipher, + int cipherKeySize, int macSize) + { + if (!TlsUtilities.IsTlsV12(context)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.context = context; + this.macSize = macSize; + + // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers + this.nonce_explicit_length = 8; + + // TODO SecurityParameters.fixed_iv_length + int fixed_iv_length = 4; + + int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length); + + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); + + int offset = 0; + + KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + byte[] client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length); + offset += fixed_iv_length; + byte[] server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length); + offset += fixed_iv_length; + + if (offset != key_block_size) + throw new TlsFatalAlert(AlertDescription.internal_error); + + KeyParameter encryptKey, decryptKey; + if (context.IsServer) + { + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + this.encryptImplicitNonce = server_write_IV; + this.decryptImplicitNonce = client_write_IV; + encryptKey = server_write_key; + decryptKey = client_write_key; + } + else + { + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + this.encryptImplicitNonce = client_write_IV; + this.decryptImplicitNonce = server_write_IV; + encryptKey = client_write_key; + decryptKey = server_write_key; + } + + byte[] dummyNonce = new byte[fixed_iv_length + nonce_explicit_length]; + + this.encryptCipher.Init(true, new AeadParameters(encryptKey, 8 * macSize, dummyNonce)); + this.decryptCipher.Init(false, new AeadParameters(decryptKey, 8 * macSize, dummyNonce)); + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + // TODO We ought to be able to ask the decryptCipher (independently of it's current state!) + return ciphertextLimit - macSize - nonce_explicit_length; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + byte[] nonce = new byte[this.encryptImplicitNonce.Length + nonce_explicit_length]; + Array.Copy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.Length); + + /* + * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number. + * + * (May need review for other AEAD ciphers). + */ + TlsUtilities.WriteUint64(seqNo, nonce, encryptImplicitNonce.Length); + + int plaintextOffset = offset; + int plaintextLength = len; + int ciphertextLength = encryptCipher.GetOutputSize(plaintextLength); + + byte[] output = new byte[nonce_explicit_length + ciphertextLength]; + Array.Copy(nonce, encryptImplicitNonce.Length, output, 0, nonce_explicit_length); + int outputPos = nonce_explicit_length; + + byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength); + AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData); + + try + { + encryptCipher.Init(true, parameters); + outputPos += encryptCipher.ProcessBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos); + outputPos += encryptCipher.DoFinal(output, outputPos); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + + if (outputPos != output.Length) + { + // NOTE: Existing AEAD cipher implementations all give exact output lengths + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return output; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + if (GetPlaintextLimit(len) < 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] nonce = new byte[this.decryptImplicitNonce.Length + nonce_explicit_length]; + Array.Copy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.Length); + Array.Copy(ciphertext, offset, nonce, decryptImplicitNonce.Length, nonce_explicit_length); + + int ciphertextOffset = offset + nonce_explicit_length; + int ciphertextLength = len - nonce_explicit_length; + int plaintextLength = decryptCipher.GetOutputSize(ciphertextLength); + + byte[] output = new byte[plaintextLength]; + int outputPos = 0; + + byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength); + AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData); + + try + { + decryptCipher.Init(false, parameters); + outputPos += decryptCipher.ProcessBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos); + outputPos += decryptCipher.DoFinal(output, outputPos); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac, e); + } + + if (outputPos != output.Length) + { + // NOTE: Existing AEAD cipher implementations all give exact output lengths + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return output; + } + + /// <exception cref="IOException"></exception> + protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len) + { + /* + * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + + * TLSCompressed.length + */ + + byte[] additional_data = new byte[13]; + TlsUtilities.WriteUint64(seqNo, additional_data, 0); + TlsUtilities.WriteUint8(type, additional_data, 8); + TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9); + TlsUtilities.WriteUint16(len, additional_data, 11); + + return additional_data; + } + } +} diff --git a/crypto/src/crypto/tls/TlsAgreementCredentials.cs b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
index 46ee4f90e..7c64072e8 100644 --- a/crypto/src/crypto/tls/TlsAgreementCredentials.cs +++ b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -3,9 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsAgreementCredentials : TlsCredentials - { - /// <exception cref="IOException"></exception> - byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey); - } + public interface TlsAgreementCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey); + } } diff --git a/crypto/src/crypto/tls/TlsBlockCipher.cs b/crypto/src/crypto/tls/TlsBlockCipher.cs
index cfbceb25e..d81b881fc 100644 --- a/crypto/src/crypto/tls/TlsBlockCipher.cs +++ b/crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -1,8 +1,6 @@ using System; using System.IO; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; @@ -10,169 +8,310 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { /// <summary> - /// A generic TLS 1.0 block cipher. This can be used for AES or 3DES for example. + /// A generic TLS 1.0-1.2 / SSLv3 block cipher. This can be used for AES or 3DES for example. /// </summary> public class TlsBlockCipher - : TlsCipher + : TlsCipher { - protected TlsClientContext context; - protected byte[] randomData; + protected readonly TlsContext context; + protected readonly byte[] randomData; + protected readonly bool useExplicitIV; + protected readonly bool encryptThenMac; - protected IBlockCipher encryptCipher; - protected IBlockCipher decryptCipher; + protected readonly IBlockCipher encryptCipher; + protected readonly IBlockCipher decryptCipher; - protected TlsMac wMac; - protected TlsMac rMac; + protected readonly TlsMac mWriteMac; + protected readonly TlsMac mReadMac; public virtual TlsMac WriteMac { - get { return wMac; } + get { return mWriteMac; } } public virtual TlsMac ReadMac { - get { return rMac; } + get { return mReadMac; } } - public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher, - IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize) + /// <exception cref="IOException"></exception> + public TlsBlockCipher(TlsContext context, IBlockCipher clientWriteCipher, IBlockCipher serverWriteCipher, + IDigest clientWriteDigest, IDigest serverWriteDigest, int cipherKeySize) { this.context = context; this.randomData = new byte[256]; - context.SecureRandom.NextBytes(randomData); + context.NonceRandomGenerator.NextBytes(randomData); - this.encryptCipher = encryptCipher; - this.decryptCipher = decryptCipher; + this.useExplicitIV = TlsUtilities.IsTlsV11(context); + this.encryptThenMac = context.SecurityParameters.encryptThenMac; - int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize() - + readDigest.GetDigestSize() + encryptCipher.GetBlockSize() - + decryptCipher.GetBlockSize(); + int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize() + + serverWriteDigest.GetDigestSize(); - SecurityParameters securityParameters = context.SecurityParameters; + // From TLS 1.1 onwards, block ciphers don't need client_write_IV + if (!useExplicitIV) + { + key_block_size += clientWriteCipher.GetBlockSize() + serverWriteCipher.GetBlockSize(); + } - byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion", - TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom), - prfSize); + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); int offset = 0; - // Init MACs - wMac = CreateTlsMac(writeDigest, keyBlock, ref offset); - rMac = CreateTlsMac(readDigest, keyBlock, ref offset); + TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.GetDigestSize()); + offset += clientWriteDigest.GetDigestSize(); + TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.GetDigestSize()); + offset += serverWriteDigest.GetDigestSize(); - // Build keys - KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize); - KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize); + KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; - // Add IVs - ParametersWithIV encryptParams = CreateParametersWithIV(encryptKey, - keyBlock, ref offset, encryptCipher.GetBlockSize()); - ParametersWithIV decryptParams = CreateParametersWithIV(decryptKey, - keyBlock, ref offset, decryptCipher.GetBlockSize()); + byte[] client_write_IV, server_write_IV; + if (useExplicitIV) + { + client_write_IV = new byte[clientWriteCipher.GetBlockSize()]; + server_write_IV = new byte[serverWriteCipher.GetBlockSize()]; + } + else + { + client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + clientWriteCipher.GetBlockSize()); + offset += clientWriteCipher.GetBlockSize(); + server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + serverWriteCipher.GetBlockSize()); + offset += serverWriteCipher.GetBlockSize(); + } - if (offset != prfSize) + if (offset != key_block_size) + { throw new TlsFatalAlert(AlertDescription.internal_error); + } - // Init Ciphers - encryptCipher.Init(true, encryptParams); - decryptCipher.Init(false, decryptParams); - } + ICipherParameters encryptParams, decryptParams; + if (context.IsServer) + { + this.mWriteMac = serverWriteMac; + this.mReadMac = clientWriteMac; + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + encryptParams = new ParametersWithIV(server_write_key, server_write_IV); + decryptParams = new ParametersWithIV(client_write_key, client_write_IV); + } + else + { + this.mWriteMac = clientWriteMac; + this.mReadMac = serverWriteMac; + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + encryptParams = new ParametersWithIV(client_write_key, client_write_IV); + decryptParams = new ParametersWithIV(server_write_key, server_write_IV); + } - protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off) - { - int len = digest.GetDigestSize(); - TlsMac mac = new TlsMac(digest, buf, off, len); - off += len; - return mac; + this.encryptCipher.Init(true, encryptParams); + this.decryptCipher.Init(false, decryptParams); } - protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len) + public virtual int GetPlaintextLimit(int ciphertextLimit) { - KeyParameter key = new KeyParameter(buf, off, len); - off += len; - return key; - } + int blockSize = encryptCipher.GetBlockSize(); + int macSize = mWriteMac.Size; - protected virtual ParametersWithIV CreateParametersWithIV(KeyParameter key, - byte[] buf, ref int off, int len) - { - ParametersWithIV ivParams = new ParametersWithIV(key, buf, off, len); - off += len; - return ivParams; + int plaintextLimit = ciphertextLimit; + + // An explicit IV consumes 1 block + if (useExplicitIV) + { + plaintextLimit -= blockSize; + } + + // Leave room for the MAC, and require block-alignment + if (encryptThenMac) + { + plaintextLimit -= macSize; + plaintextLimit -= plaintextLimit % blockSize; + } + else + { + plaintextLimit -= plaintextLimit % blockSize; + plaintextLimit -= macSize; + } + + // Minimum 1 byte of padding + --plaintextLimit; + + return plaintextLimit; } - public virtual byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len) + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) { - int blocksize = encryptCipher.GetBlockSize(); - int padding_length = blocksize - 1 - ((len + wMac.Size) % blocksize); + int blockSize = encryptCipher.GetBlockSize(); + int macSize = mWriteMac.Size; + + ProtocolVersion version = context.ServerVersion; + + int enc_input_length = len; + if (!encryptThenMac) + { + enc_input_length += macSize; + } - //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion; - bool isTls = true; + int padding_length = blockSize - 1 - (enc_input_length % blockSize); - if (isTls) + // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though) + if (!version.IsDtls && !version.IsSsl) { // Add a random number of extra blocks worth of padding - int maxExtraPadBlocks = (255 - padding_length) / blocksize; + int maxExtraPadBlocks = (255 - padding_length) / blockSize; int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks); - padding_length += actualExtraPadBlocks * blocksize; + padding_length += actualExtraPadBlocks * blockSize; + } + + int totalSize = len + macSize + padding_length + 1; + if (useExplicitIV) + { + totalSize += blockSize; + } + + byte[] outBuf = new byte[totalSize]; + int outOff = 0; + + if (useExplicitIV) + { + byte[] explicitIV = new byte[blockSize]; + context.NonceRandomGenerator.NextBytes(explicitIV); + + encryptCipher.Init(true, new ParametersWithIV(null, explicitIV)); + + Array.Copy(explicitIV, 0, outBuf, outOff, blockSize); + outOff += blockSize; + } + + int blocks_start = outOff; + + Array.Copy(plaintext, offset, outBuf, outOff, len); + outOff += len; + + if (!encryptThenMac) + { + byte[] mac = mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len); + Array.Copy(mac, 0, outBuf, outOff, mac.Length); + outOff += mac.Length; } - int totalsize = len + wMac.Size + padding_length + 1; - byte[] outbuf = new byte[totalsize]; - Array.Copy(plaintext, offset, outbuf, 0, len); - byte[] mac = wMac.CalculateMac(type, plaintext, offset, len); - Array.Copy(mac, 0, outbuf, len, mac.Length); - int paddoffset = len + mac.Length; for (int i = 0; i <= padding_length; i++) { - outbuf[i + paddoffset] = (byte)padding_length; + outBuf[outOff++] = (byte)padding_length; } - for (int i = 0; i < totalsize; i += blocksize) + + for (int i = blocks_start; i < outOff; i += blockSize) + { + encryptCipher.ProcessBlock(outBuf, i, outBuf, i); + } + + if (encryptThenMac) { - encryptCipher.ProcessBlock(outbuf, i, outbuf, i); + byte[] mac = mWriteMac.CalculateMac(seqNo, type, outBuf, 0, outOff); + Array.Copy(mac, 0, outBuf, outOff, mac.Length); + outOff += mac.Length; } - return outbuf; + + // assert outBuf.length == outOff; + + return outBuf; } - public virtual byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len) + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) { int blockSize = decryptCipher.GetBlockSize(); - int macSize = rMac.Size; + int macSize = mReadMac.Size; + + int minLen = blockSize; + if (encryptThenMac) + { + minLen += macSize; + } + else + { + minLen = System.Math.Max(minLen, macSize + 1); + } - /* - * TODO[TLS 1.1] Explicit IV implies minLen = blockSize + max(blockSize, macSize + 1), - * and will need further changes to offset and plen variables below. - */ + if (useExplicitIV) + { + minLen += blockSize; + } - int minLen = System.Math.Max(blockSize, macSize + 1); if (len < minLen) throw new TlsFatalAlert(AlertDescription.decode_error); - if (len % blockSize != 0) + int blocks_length = len; + if (encryptThenMac) + { + blocks_length -= macSize; + } + + if (blocks_length % blockSize != 0) throw new TlsFatalAlert(AlertDescription.decryption_failed); - for (int i = 0; i < len; i += blockSize) + if (encryptThenMac) { - decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i); + int end = offset + len; + byte[] receivedMac = Arrays.CopyOfRange(ciphertext, end - macSize, end); + byte[] calculatedMac = mReadMac.CalculateMac(seqNo, type, ciphertext, offset, len - macSize); + + bool badMacEtm = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac); + if (badMacEtm) + { + /* + * RFC 7366 3. The MAC SHALL be evaluated before any further processing such as + * decryption is performed, and if the MAC verification fails, then processing SHALL + * terminate immediately. For TLS, a fatal bad_record_mac MUST be generated [2]. For + * DTLS, the record MUST be discarded, and a fatal bad_record_mac MAY be generated + * [4]. This immediate response to a bad MAC eliminates any timing channels that may + * be available through the use of manipulated packet data. + */ + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } } - int plen = len; + if (useExplicitIV) + { + decryptCipher.Init(false, new ParametersWithIV(null, ciphertext, offset, blockSize)); - // If there's anything wrong with the padding, this will return zero - int totalPad = CheckPaddingConstantTime(ciphertext, offset, plen, blockSize, macSize); + offset += blockSize; + blocks_length -= blockSize; + } - int macInputLen = plen - totalPad - macSize; + for (int i = 0; i < blocks_length; i += blockSize) + { + decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i); + } + + // If there's anything wrong with the padding, this will return zero + int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMac ? 0 : macSize); + bool badMac = (totalPad == 0); - byte[] decryptedMac = Arrays.Copy(ciphertext, offset + macInputLen, macSize); - byte[] calculatedMac = rMac.CalculateMacConstantTime(type, ciphertext, offset, macInputLen, plen - macSize, randomData); + int dec_output_length = blocks_length - totalPad; - bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac); + if (!encryptThenMac) + { + dec_output_length -= macSize; + int macInputLen = dec_output_length; + int macOff = offset + macInputLen; + byte[] receivedMac = Arrays.CopyOfRange(ciphertext, macOff, macOff + macSize); + byte[] calculatedMac = mReadMac.CalculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, + blocks_length - macSize, randomData); + + badMac |= !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac); + } - if (badMac || totalPad == 0) + if (badMac) throw new TlsFatalAlert(AlertDescription.bad_record_mac); - return Arrays.Copy(ciphertext, offset, macInputLen); + return Arrays.CopyOfRange(ciphertext, offset, offset + dec_output_length); } protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize) @@ -185,10 +324,7 @@ namespace Org.BouncyCastle.Crypto.Tls int dummyIndex = 0; byte padDiff = 0; - //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion; - bool isTls = true; - - if ((!isTls && totalPad > blockSize) || (macSize + totalPad > len)) + if ((TlsUtilities.IsSsl(context) && totalPad > blockSize) || (macSize + totalPad > len)) { totalPad = 0; } @@ -225,25 +361,24 @@ namespace Org.BouncyCastle.Crypto.Tls protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max) { -// return r.NextInt(max + 1); + // return r.NextInt(max + 1); - uint x = (uint)r.NextInt(); + int x = r.NextInt(); int n = LowestBitSet(x); return System.Math.Min(n, max); } - private int LowestBitSet(uint x) + protected virtual int LowestBitSet(int x) { if (x == 0) - { return 32; - } + uint ux = (uint)x; int n = 0; - while ((x & 1) == 0) + while ((ux & 1U) == 0) { ++n; - x >>= 1; + ux >>= 1; } return n; } diff --git a/crypto/src/crypto/tls/TlsCipher.cs b/crypto/src/crypto/tls/TlsCipher.cs
index a58f4943f..7bd8573ac 100644 --- a/crypto/src/crypto/tls/TlsCipher.cs +++ b/crypto/src/crypto/tls/TlsCipher.cs
@@ -5,10 +5,12 @@ namespace Org.BouncyCastle.Crypto.Tls { public interface TlsCipher { + int GetPlaintextLimit(int ciphertextLimit); + /// <exception cref="IOException"></exception> - byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len); + byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len); /// <exception cref="IOException"></exception> - byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len); + byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len); } } diff --git a/crypto/src/crypto/tls/TlsCipherFactory.cs b/crypto/src/crypto/tls/TlsCipherFactory.cs
index bd65f8b4b..4e1fe0eb9 100644 --- a/crypto/src/crypto/tls/TlsCipherFactory.cs +++ b/crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -6,7 +6,6 @@ namespace Org.BouncyCastle.Crypto.Tls public interface TlsCipherFactory { /// <exception cref="IOException"></exception> - TlsCipher CreateCipher(TlsClientContext context, int encryptionAlgorithm, - DigestAlgorithm digestAlgorithm); + TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm); } } diff --git a/crypto/src/crypto/tls/TlsClient.cs b/crypto/src/crypto/tls/TlsClient.cs
index a4cdc647d..116f6a779 100644 --- a/crypto/src/crypto/tls/TlsClient.cs +++ b/crypto/src/crypto/tls/TlsClient.cs
@@ -15,6 +15,20 @@ namespace Org.BouncyCastle.Crypto.Tls /// </param> void Init(TlsClientContext context); + /// <summary>Return the session this client wants to resume, if any.</summary> + /// <remarks>Note that the peer's certificate chain for the session (if any) may need to be periodically revalidated.</remarks> + /// <returns> + /// A <see cref="TlsSession"/> representing the resumable session to be used for this connection, + /// or null to use a new session. + /// </returns> + TlsSession GetSessionToResume(); + + ProtocolVersion ClientHelloRecordLayerVersion { get; } + + ProtocolVersion ClientVersion { get; } + + bool IsFallback { get; } + /// <summary> /// Get the list of cipher suites that this client supports. /// </summary> @@ -40,12 +54,13 @@ namespace Org.BouncyCastle.Crypto.Tls /// <exception cref="IOException"></exception> IDictionary GetClientExtensions(); + /// <exception cref="IOException"></exception> + void NotifyServerVersion(ProtocolVersion selectedVersion); + /// <summary> - /// Reports the session ID once it has been determined. + /// Notifies the client of the session_id sent in the ServerHello. /// </summary> - /// <param name="sessionID"> - /// A <see cref="System.Byte"/> - /// </param> + /// <param name="sessionID">An array of <see cref="System.Byte"/></param> void NotifySessionID(byte[] sessionID); /// <summary> @@ -73,18 +88,6 @@ namespace Org.BouncyCastle.Crypto.Tls void NotifySelectedCompressionMethod(byte selectedCompressionMethod); /// <summary> - /// Report whether the server supports secure renegotiation - /// </summary> - /// <remarks> - /// The protocol handler automatically processes the relevant extensions - /// </remarks> - /// <param name="secureRenegotiation"> - /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation - /// </param> - /// <exception cref="IOException"></exception> - void NotifySecureRenegotiation(bool secureRenegotiation); - - /// <summary> /// Report the extensions from an extended server hello. /// </summary> /// <remarks> @@ -95,6 +98,10 @@ namespace Org.BouncyCastle.Crypto.Tls /// </param> void ProcessServerExtensions(IDictionary serverExtensions); + /// <param name="serverSupplementalData">A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></param> + /// <exception cref="IOException"/> + void ProcessServerSupplementalData(IList serverSupplementalData); + /// <summary> /// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange /// part of the protocol. @@ -112,19 +119,18 @@ namespace Org.BouncyCastle.Crypto.Tls /// <exception cref="IOException"/> TlsAuthentication GetAuthentication(); - /// <summary> - /// Return an implementation of <see cref="TlsCompression"/> to handle record compression. - /// </summary> + /// <returns>A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></returns> /// <exception cref="IOException"/> - TlsCompression GetCompression(); + IList GetClientSupplementalData(); - /// <summary> - /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption. - /// </summary> - /// <returns> - /// A <see cref="TlsCipher"/> - /// </returns> + /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message</summary> + /// <remarks> + /// This method will be called (only) when a NewSessionTicket handshake message is received. The + /// ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption + /// that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>. + /// </remarks> + /// <param name="newSessionTicket">The <see cref="NewSessionTicket">ticket</see></param> /// <exception cref="IOException"/> - TlsCipher GetCipher(); + void NotifyNewSessionTicket(NewSessionTicket newSessionTicket); } } diff --git a/crypto/src/crypto/tls/TlsClientContext.cs b/crypto/src/crypto/tls/TlsClientContext.cs
index dbb10aa76..b077d0aaf 100644 --- a/crypto/src/crypto/tls/TlsClientContext.cs +++ b/crypto/src/crypto/tls/TlsClientContext.cs
@@ -4,12 +4,8 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsClientContext - { - SecureRandom SecureRandom { get; } - - SecurityParameters SecurityParameters { get; } - - object UserObject { get; set; } - } + public interface TlsClientContext + : TlsContext + { + } } diff --git a/crypto/src/crypto/tls/TlsClientContextImpl.cs b/crypto/src/crypto/tls/TlsClientContextImpl.cs
index 9d5dee232..674d68937 100644 --- a/crypto/src/crypto/tls/TlsClientContextImpl.cs +++ b/crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -4,34 +4,17 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsClientContextImpl - : TlsClientContext - { - private readonly SecureRandom secureRandom; - private readonly SecurityParameters securityParameters; + internal class TlsClientContextImpl + : AbstractTlsContext, TlsClientContext + { + internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + : base(secureRandom, securityParameters) + { + } - private object userObject = null; - - internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) - { - this.secureRandom = secureRandom; - this.securityParameters = securityParameters; - } - - public virtual SecureRandom SecureRandom - { - get { return secureRandom; } - } - - public virtual SecurityParameters SecurityParameters - { - get { return securityParameters; } - } - - public virtual object UserObject - { - get { return userObject; } - set { this.userObject = value; } - } - } + public override bool IsServer + { + get { return false; } + } + } } diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs new file mode 100644
index 000000000..14c1cf4a4 --- /dev/null +++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -0,0 +1,906 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsClientProtocol + : TlsProtocol + { + protected TlsClient mTlsClient = null; + internal TlsClientContextImpl mTlsClientContext = null; + + protected byte[] mSelectedSessionID = null; + + protected TlsKeyExchange mKeyExchange = null; + protected TlsAuthentication mAuthentication = null; + + protected CertificateStatus mCertificateStatus = null; + protected CertificateRequest mCertificateRequest = null; + + /** + * Constructor for blocking mode. + * @param stream The bi-directional stream of data to/from the server + * @param secureRandom Random number generator for various cryptographic functions + */ + public TlsClientProtocol(Stream stream, SecureRandom secureRandom) + : base(stream, secureRandom) + { + } + + /** + * Constructor for blocking mode. + * @param input The stream of data from the server + * @param output The stream of data to the server + * @param secureRandom Random number generator for various cryptographic functions + */ + public TlsClientProtocol(Stream input, Stream output, SecureRandom secureRandom) + : base(input, output, secureRandom) + { + } + + /** + * Constructor for non-blocking mode.<br> + * <br> + * When data is received, use {@link #offerInput(java.nio.ByteBuffer)} to + * provide the received ciphertext, then use + * {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br> + * <br> + * Similarly, when data needs to be sent, use + * {@link #offerOutput(byte[], int, int)} to provide the cleartext, then use + * {@link #readOutput(byte[], int, int)} to get the corresponding + * ciphertext. + * + * @param secureRandom + * Random number generator for various cryptographic functions + */ + public TlsClientProtocol(SecureRandom secureRandom) + : base(secureRandom) + { + } + + /** + * Initiates a TLS handshake in the role of client.<br> + * <br> + * In blocking mode, this will not return until the handshake is complete. + * In non-blocking mode, use {@link TlsPeer#NotifyHandshakeComplete()} to + * receive a callback when the handshake is complete. + * + * @param tlsClient The {@link TlsClient} to use for the handshake. + * @throws IOException If in blocking mode and handshake was not successful. + */ + public virtual void Connect(TlsClient tlsClient) + { + if (tlsClient == null) + throw new ArgumentNullException("tlsClient"); + if (this.mTlsClient != null) + throw new InvalidOperationException("'Connect' can only be called once"); + + this.mTlsClient = tlsClient; + + this.mSecurityParameters = new SecurityParameters(); + this.mSecurityParameters.entity = ConnectionEnd.client; + + this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters); + + this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), + mTlsClientContext.NonceRandomGenerator); + + this.mTlsClient.Init(mTlsClientContext); + this.mRecordStream.Init(mTlsClientContext); + + TlsSession sessionToResume = tlsClient.GetSessionToResume(); + if (sessionToResume != null && sessionToResume.IsResumable) + { + SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); + if (sessionParameters != null) + { + this.mTlsSession = sessionToResume; + this.mSessionParameters = sessionParameters; + } + } + + SendClientHelloMessage(); + this.mConnectionState = CS_CLIENT_HELLO; + + BlockForHandshake(); + } + + protected override void CleanupHandshake() + { + base.CleanupHandshake(); + + this.mSelectedSessionID = null; + this.mKeyExchange = null; + this.mAuthentication = null; + this.mCertificateStatus = null; + this.mCertificateRequest = null; + } + + protected override TlsContext Context + { + get { return mTlsClientContext; } + } + + internal override AbstractTlsContext ContextAdmin + { + get { return mTlsClientContext; } + } + + protected override TlsPeer Peer + { + get { return mTlsClient; } + } + + protected override void HandleHandshakeMessage(byte type, byte[] data) + { + MemoryStream buf = new MemoryStream(data, false); + + if (this.mResumedSession) + { + if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ProcessFinishedMessage(buf); + this.mConnectionState = CS_SERVER_FINISHED; + + SendFinishedMessage(); + this.mConnectionState = CS_CLIENT_FINISHED; + this.mConnectionState = CS_END; + + CompleteHandshake(); + return; + } + + switch (type) + { + case HandshakeType.certificate: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + case CS_SERVER_SUPPLEMENTAL_DATA: + { + if (this.mConnectionState == CS_SERVER_HELLO) + { + HandleSupplementalData(null); + } + + // Parse the Certificate message and Send to cipher suite + + this.mPeerCertificate = Certificate.Parse(buf); + + AssertEmpty(buf); + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty) + { + this.mAllowCertificateStatus = false; + } + + this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate); + + this.mAuthentication = mTlsClient.GetAuthentication(); + this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_SERVER_CERTIFICATE; + break; + } + case HandshakeType.certificate_status: + { + switch (this.mConnectionState) + { + case CS_SERVER_CERTIFICATE: + { + if (!this.mAllowCertificateStatus) + { + /* + * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the + * server MUST have included an extension of type "status_request" with empty + * "extension_data" in the extended server hello.. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mCertificateStatus = CertificateStatus.Parse(buf); + + AssertEmpty(buf); + + // TODO[RFC 3546] Figure out how to provide this to the client/authentication. + + this.mConnectionState = CS_CERTIFICATE_STATUS; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.finished: + { + switch (this.mConnectionState) + { + case CS_CLIENT_FINISHED: + case CS_SERVER_SESSION_TICKET: + { + if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket) + { + /* + * RFC 5077 3.3. This message MUST be sent if the server included a + * SessionTicket extension in the ServerHello. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + ProcessFinishedMessage(buf); + this.mConnectionState = CS_SERVER_FINISHED; + this.mConnectionState = CS_END; + + CompleteHandshake(); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.server_hello: + { + switch (this.mConnectionState) + { + case CS_CLIENT_HELLO: + { + ReceiveServerHelloMessage(buf); + this.mConnectionState = CS_SERVER_HELLO; + + this.mRecordStream.NotifyHelloComplete(); + + ApplyMaxFragmentLengthExtension(); + + if (this.mResumedSession) + { + this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret); + this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); + + SendChangeCipherSpecMessage(); + } + else + { + InvalidateSession(); + + if (this.mSelectedSessionID.Length > 0) + { + this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null); + } + } + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.supplemental_data: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + { + HandleSupplementalData(ReadSupplementalDataMessage(buf)); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.server_hello_done: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + case CS_SERVER_SUPPLEMENTAL_DATA: + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + case CS_SERVER_KEY_EXCHANGE: + case CS_CERTIFICATE_REQUEST: + { + if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA) + { + HandleSupplementalData(null); + } + + if (mConnectionState < CS_SERVER_CERTIFICATE) + { + // There was no server certificate message; check it's OK + this.mKeyExchange.SkipServerCredentials(); + this.mAuthentication = null; + } + + if (mConnectionState < CS_SERVER_KEY_EXCHANGE) + { + // There was no server key exchange message; check it's OK + this.mKeyExchange.SkipServerKeyExchange(); + } + + AssertEmpty(buf); + + this.mConnectionState = CS_SERVER_HELLO_DONE; + + this.mRecordStream.HandshakeHash.SealHashAlgorithms(); + + IList clientSupplementalData = mTlsClient.GetClientSupplementalData(); + if (clientSupplementalData != null) + { + SendSupplementalDataMessage(clientSupplementalData); + } + this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA; + + TlsCredentials clientCreds = null; + if (mCertificateRequest == null) + { + this.mKeyExchange.SkipClientCredentials(); + } + else + { + clientCreds = this.mAuthentication.GetClientCredentials(mCertificateRequest); + + if (clientCreds == null) + { + this.mKeyExchange.SkipClientCredentials(); + + /* + * RFC 5246 If no suitable certificate is available, the client MUST Send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + SendCertificateMessage(Certificate.EmptyChain); + } + else + { + this.mKeyExchange.ProcessClientCredentials(clientCreds); + + SendCertificateMessage(clientCreds.Certificate); + } + } + + this.mConnectionState = CS_CLIENT_CERTIFICATE; + + /* + * Send the client key exchange message, depending on the key exchange we are using + * in our CipherSuite. + */ + SendClientKeyExchangeMessage(); + this.mConnectionState = CS_CLIENT_KEY_EXCHANGE; + + TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish(); + this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, prepareFinishHash, null); + + EstablishMasterSecret(Context, mKeyExchange); + mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); + + if (clientCreds != null && clientCreds is TlsSignerCredentials) + { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds; + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm( + Context, signerCredentials); + + byte[] hash; + if (signatureAndHashAlgorithm == null) + { + hash = mSecurityParameters.SessionHash; + } + else + { + hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash); + } + + byte[] signature = signerCredentials.GenerateCertificateSignature(hash); + DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature); + SendCertificateVerifyMessage(certificateVerify); + + this.mConnectionState = CS_CERTIFICATE_VERIFY; + } + + SendChangeCipherSpecMessage(); + SendFinishedMessage(); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + this.mConnectionState = CS_CLIENT_FINISHED; + break; + } + case HandshakeType.server_key_exchange: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + case CS_SERVER_SUPPLEMENTAL_DATA: + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + { + if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA) + { + HandleSupplementalData(null); + } + + if (mConnectionState < CS_SERVER_CERTIFICATE) + { + // There was no server certificate message; check it's OK + this.mKeyExchange.SkipServerCredentials(); + this.mAuthentication = null; + } + + this.mKeyExchange.ProcessServerKeyExchange(buf); + + AssertEmpty(buf); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_SERVER_KEY_EXCHANGE; + break; + } + case HandshakeType.certificate_request: + { + switch (this.mConnectionState) + { + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + case CS_SERVER_KEY_EXCHANGE: + { + if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE) + { + // There was no server key exchange message; check it's OK + this.mKeyExchange.SkipServerKeyExchange(); + } + + if (this.mAuthentication == null) + { + /* + * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server + * to request client identification. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + this.mCertificateRequest = CertificateRequest.Parse(Context, buf); + + AssertEmpty(buf); + + this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest); + + /* + * TODO Give the client a chance to immediately select the CertificateVerify hash + * algorithm here to avoid tracking the other hash algorithms unnecessarily? + */ + TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash, + this.mCertificateRequest.SupportedSignatureAlgorithms); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_CERTIFICATE_REQUEST; + break; + } + case HandshakeType.session_ticket: + { + switch (this.mConnectionState) + { + case CS_CLIENT_FINISHED: + { + if (!this.mExpectSessionTicket) + { + /* + * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a + * SessionTicket extension in the ServerHello. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + /* + * RFC 5077 3.4. If the client receives a session ticket from the server, then it + * discards any Session ID that was sent in the ServerHello. + */ + InvalidateSession(); + + ReceiveNewSessionTicketMessage(buf); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_SERVER_SESSION_TICKET; + break; + } + case HandshakeType.hello_request: + { + AssertEmpty(buf); + + /* + * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the + * client is currently negotiating a session. This message may be ignored by the client + * if it does not wish to renegotiate a session, or the client may, if it wishes, + * respond with a no_renegotiation alert. + */ + if (this.mConnectionState == CS_END) + { + RefuseRenegotiation(); + } + break; + } + case HandshakeType.client_hello: + case HandshakeType.client_key_exchange: + case HandshakeType.certificate_verify: + case HandshakeType.hello_verify_request: + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + protected virtual void HandleSupplementalData(IList serverSupplementalData) + { + this.mTlsClient.ProcessServerSupplementalData(serverSupplementalData); + this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA; + + this.mKeyExchange = mTlsClient.GetKeyExchange(); + this.mKeyExchange.Init(Context); + } + + protected virtual void ReceiveNewSessionTicketMessage(MemoryStream buf) + { + NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf); + + AssertEmpty(buf); + + mTlsClient.NotifyNewSessionTicket(newSessionTicket); + } + + protected virtual void ReceiveServerHelloMessage(MemoryStream buf) + { + { + ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); + if (server_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + // Check that this matches what the server is Sending in the record layer + if (!server_version.Equals(this.mRecordStream.ReadVersion)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + ProtocolVersion client_version = Context.ClientVersion; + if (!server_version.IsEqualOrEarlierVersionOf(client_version)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mRecordStream.SetWriteVersion(server_version); + ContextAdmin.SetServerVersion(server_version); + this.mTlsClient.NotifyServerVersion(server_version); + } + + /* + * Read the server random + */ + this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); + + this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); + if (this.mSelectedSessionID.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + this.mTlsClient.NotifySessionID(this.mSelectedSessionID); + this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null + && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID); + + /* + * Find out which CipherSuite the server has chosen and check that it was one of the offered + * ones, and is a valid selection for the negotiated version. + */ + int selectedCipherSuite = TlsUtilities.ReadUint16(buf); + if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || CipherSuite.IsScsv(selectedCipherSuite) + || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite); + + /* + * Find out which CompressionMethod the server has chosen and check that it was one of the + * offered ones. + */ + byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); + if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod); + + /* + * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server + * hello message when the client has requested extended functionality via the extended + * client hello message specified in Section 2.1. ... Note that the extended server hello + * message is only sent in response to an extended client hello message. This prevents the + * possibility that the extended server hello message could "break" existing TLS 1.0 + * clients. + */ + this.mServerExtensions = ReadExtensions(buf); + + /* + * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an + * extended client hello message. + * + * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server + * Hello is always allowed. + */ + if (this.mServerExtensions != null) + { + foreach (int extType in this.mServerExtensions.Keys) + { + /* + * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a + * ClientHello containing only the SCSV is an explicit exception to the prohibition + * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is + * only allowed because the client is signaling its willingness to receive the + * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + if (extType == ExtensionType.renegotiation_info) + continue; + + /* + * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the + * same extension type appeared in the corresponding ClientHello. If a client + * receives an extension type in ServerHello that it did not request in the + * associated ClientHello, it MUST abort the handshake with an unsupported_extension + * fatal alert. + */ + if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType)) + throw new TlsFatalAlert(AlertDescription.unsupported_extension); + + /* + * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and Send a server hello containing no + * extensions[.] + */ + if (this.mResumedSession) + { + // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats + // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats + // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats + // throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + /* + * RFC 5746 3.4. Client Behavior: Initial Handshake + */ + { + /* + * When a ServerHello is received, the client MUST check if it includes the + * "renegotiation_info" extension: + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); + if (renegExtData != null) + { + /* + * If the extension is present, set the secure_renegotiation flag to TRUE. The + * client MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal + * handshake_failure alert). + */ + this.mSecureRenegotiation = true; + + if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming + this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation); + + IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions; + if (this.mResumedSession) + { + if (selectedCipherSuite != this.mSessionParameters.CipherSuite + || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + sessionClientExtensions = null; + sessionServerExtensions = this.mSessionParameters.ReadServerExtensions(); + } + + this.mSecurityParameters.cipherSuite = selectedCipherSuite; + this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; + + if (sessionServerExtensions != null) + { + { + /* + * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client + * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) + * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the + * client. + */ + bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions); + if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC; + } + + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); + + this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions, + sessionServerExtensions, AlertDescription.illegal_parameter); + + this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + this.mAllowCertificateStatus = !this.mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request, + AlertDescription.illegal_parameter); + + this.mExpectSessionTicket = !this.mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket, + AlertDescription.illegal_parameter); + } + + /* + * TODO[session-hash] + * + * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes + * that do not use the extended master secret [..]. (and see 5.2, 5.3) + */ + + if (sessionClientExtensions != null) + { + this.mTlsClient.ProcessServerExtensions(sessionServerExtensions); + } + + this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, this.mSecurityParameters.CipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify + * verify_data_length has a verify_data_length equal to 12. This includes all + * existing cipher suites. + */ + this.mSecurityParameters.verifyDataLength = 12; + } + + protected virtual void SendCertificateVerifyMessage(DigitallySigned certificateVerify) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify); + + certificateVerify.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendClientHelloMessage() + { + this.mRecordStream.SetWriteVersion(this.mTlsClient.ClientHelloRecordLayerVersion); + + ProtocolVersion client_version = this.mTlsClient.ClientVersion; + if (client_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ContextAdmin.SetClientVersion(client_version); + + /* + * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a + * Session ID in the TLS ClientHello. + */ + byte[] session_id = TlsUtilities.EmptyBytes; + if (this.mTlsSession != null) + { + session_id = this.mTlsSession.SessionID; + if (session_id == null || session_id.Length > 32) + { + session_id = TlsUtilities.EmptyBytes; + } + } + + bool fallback = this.mTlsClient.IsFallback; + + this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites(); + + this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods(); + + if (session_id.Length > 0 && this.mSessionParameters != null) + { + if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite) + || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm)) + { + session_id = TlsUtilities.EmptyBytes; + } + } + + this.mClientExtensions = this.mTlsClient.GetClientExtensions(); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); + + TlsUtilities.WriteVersion(client_version, message); + + message.Write(this.mSecurityParameters.ClientRandom); + + TlsUtilities.WriteOpaque8(session_id, message); + + // Cipher Suites (and SCSV) + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info); + bool noRenegExt = (null == renegExtData); + + bool noRenegScsv = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if (noRenegExt && noRenegScsv) + { + // TODO Consider whether to default to a client extension instead + // this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions); + // this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); + this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + /* + * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version + * containing a lower value than the latest (highest-valued) version supported by the + * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in + * ClientHello.cipher_suites. + */ + if (fallback && !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)) + { + this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV); + } + + TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message); + } + + TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message); + + if (mClientExtensions != null) + { + WriteExtensions(message, mClientExtensions); + } + + message.WriteToRecordStream(this); + } + + protected virtual void SendClientKeyExchangeMessage() + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange); + + this.mKeyExchange.GenerateClientKeyExchange(message); + + message.WriteToRecordStream(this); + } + } +} diff --git a/crypto/src/crypto/tls/TlsContext.cs b/crypto/src/crypto/tls/TlsContext.cs new file mode 100644
index 000000000..d066723fc --- /dev/null +++ b/crypto/src/crypto/tls/TlsContext.cs
@@ -0,0 +1,45 @@ +using System; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsContext + { + IRandomGenerator NonceRandomGenerator { get; } + + SecureRandom SecureRandom { get; } + + SecurityParameters SecurityParameters { get; } + + bool IsServer { get; } + + ProtocolVersion ClientVersion { get; } + + ProtocolVersion ServerVersion { get; } + + /** + * Used to get the resumable session, if any, used by this connection. Only available after the + * handshake has successfully completed. + * + * @return A {@link TlsSession} representing the resumable session used by this connection, or + * null if no resumable session available. + * @see TlsPeer#NotifyHandshakeComplete() + */ + TlsSession ResumableSession { get; } + + object UserObject { get; set; } + + /** + * Export keying material according to RFC 5705: "Keying Material Exporters for TLS". + * + * @param asciiLabel indicates which application will use the exported keys. + * @param context_value allows the application using the exporter to mix its own data with the TLS PRF for + * the exporter output. + * @param length the number of bytes to generate + * @return a pseudorandom bit string of 'length' bytes generated from the master_secret. + */ + byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length); + } +} diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
index 26d76fd3d..93ef1fa4a 100644 --- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -1,86 +1,98 @@ 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; namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// TLS 1.0 DH key exchange. - /// </summary> - internal class TlsDHKeyExchange - : TlsKeyExchange + /// <summary>(D)TLS DH key exchange.</summary> + public class TlsDHKeyExchange + : AbstractTlsKeyExchange { - protected TlsClientContext context; - protected int keyExchange; - protected TlsSigner tlsSigner; + protected TlsSigner mTlsSigner; + protected DHParameters mDHParameters; - protected AsymmetricKeyParameter serverPublicKey = null; - protected DHPublicKeyParameters dhAgreeServerPublicKey = null; - protected TlsAgreementCredentials agreementCredentials; - protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null; + protected AsymmetricKeyParameter mServerPublicKey; + protected TlsAgreementCredentials mAgreementCredentials; - internal TlsDHKeyExchange(TlsClientContext context, int keyExchange) + protected DHPrivateKeyParameters mDHAgreePrivateKey; + protected DHPublicKeyParameters mDHAgreePublicKey; + + public TlsDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters) + : base(keyExchange, supportedSignatureAlgorithms) { switch (keyExchange) { - case KeyExchangeAlgorithm.DH_RSA: - case KeyExchangeAlgorithm.DH_DSS: - this.tlsSigner = null; - break; - case KeyExchangeAlgorithm.DHE_RSA: - this.tlsSigner = new TlsRsaSigner(); - break; - case KeyExchangeAlgorithm.DHE_DSS: - this.tlsSigner = new TlsDssSigner(); - break; - default: - throw new ArgumentException("unsupported key exchange algorithm", "keyExchange"); + case KeyExchangeAlgorithm.DH_RSA: + case KeyExchangeAlgorithm.DH_DSS: + this.mTlsSigner = null; + break; + case KeyExchangeAlgorithm.DHE_RSA: + this.mTlsSigner = new TlsRsaSigner(); + break; + case KeyExchangeAlgorithm.DHE_DSS: + this.mTlsSigner = new TlsDssSigner(); + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); } - this.context = context; - this.keyExchange = keyExchange; + this.mDHParameters = dhParameters; + } + + public override void Init(TlsContext context) + { + base.Init(context); + + if (this.mTlsSigner != null) + { + this.mTlsSigner.Init(context); + } } - public virtual void SkipServerCertificate() + public override void SkipServerCredentials() { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public virtual void ProcessServerCertificate(Certificate serverCertificate) + public override void ProcessServerCertificate(Certificate serverCertificate) { + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); - SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; try { - this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); } - catch (Exception) + catch (Exception e) { - throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); } - if (tlsSigner == null) + if (mTlsSigner == null) { try { - this.dhAgreeServerPublicKey = ValidateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey); + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey((DHPublicKeyParameters)this.mServerPublicKey); + this.mDHParameters = ValidateDHParameters(mDHAgreePublicKey.Parameters); } - catch (InvalidCastException) + catch (InvalidCastException e) { - throw new TlsFatalAlert(AlertDescription.certificate_unknown); + throw new TlsFatalAlert(AlertDescription.certificate_unknown, e); } TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement); } else { - if (!tlsSigner.IsValidPublicKey(this.serverPublicKey)) + if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey)) { throw new TlsFatalAlert(AlertDescription.certificate_unknown); } @@ -88,55 +100,51 @@ namespace Org.BouncyCastle.Crypto.Tls TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); } - // TODO - /* - * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the - * signing algorithm for the certificate must be the same as the algorithm for the - * certificate key." - */ + base.ProcessServerCertificate(serverCertificate); } - public virtual void SkipServerKeyExchange() + public override bool RequiresServerKeyExchange { - // OK - } - - public virtual void ProcessServerKeyExchange(Stream input) - { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + get + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.DH_anon: + return true; + default: + return false; + } + } } - public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { byte[] types = certificateRequest.CertificateTypes; - foreach (byte type in types) + for (int i = 0; i < types.Length; ++i) { - switch (type) + switch (types[i]) { - case ClientCertificateType.rsa_sign: - case ClientCertificateType.dss_sign: - case ClientCertificateType.rsa_fixed_dh: - case ClientCertificateType.dss_fixed_dh: - case ClientCertificateType.ecdsa_sign: - break; - default: - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.rsa_fixed_dh: + case ClientCertificateType.dss_fixed_dh: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } - public virtual void SkipClientCredentials() - { - this.agreementCredentials = null; - } - - public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { if (clientCredentials is TlsAgreementCredentials) { // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')? - this.agreementCredentials = (TlsAgreementCredentials)clientCredentials; + this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials; } else if (clientCredentials is TlsSignerCredentials) { @@ -148,54 +156,69 @@ namespace Org.BouncyCastle.Crypto.Tls } } - public virtual void GenerateClientKeyExchange(Stream output) + public override void GenerateClientKeyExchange(Stream output) { /* - * RFC 2246 7.4.7.2 If the client certificate already contains a suitable - * Diffie-Hellman key, then Yc is implicit and does not need to be sent again. In - * this case, the Client Key Exchange message will be sent, but will be empty. + * RFC 2246 7.4.7.2 If the client certificate already contains a suitable Diffie-Hellman + * key, then Yc is implicit and does not need to be sent again. In this case, the Client Key + * Exchange message will be sent, but will be empty. */ - if (agreementCredentials == null) + if (mAgreementCredentials == null) { - GenerateEphemeralClientKeyExchange(dhAgreeServerPublicKey.Parameters, output); + this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom, + mDHParameters, output); } } - public virtual byte[] GeneratePremasterSecret() + public override void ProcessClientCertificate(Certificate clientCertificate) { - if (agreementCredentials != null) - { - return agreementCredentials.GenerateAgreement(dhAgreeServerPublicKey); - } + // TODO Extract the public key and validate - return CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); - } - - protected virtual bool AreCompatibleParameters(DHParameters a, DHParameters b) - { - return a.P.Equals(b.P) && a.G.Equals(b.G); + /* + * TODO If the certificate is 'fixed', take the public key as dhAgreePublicKey and check + * that the parameters match the server's (see 'areCompatibleParameters'). + */ } - protected virtual byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey, - DHPrivateKeyParameters privateKey) + public override void ProcessClientKeyExchange(Stream input) { - return TlsDHUtilities.CalculateDHBasicAgreement(publicKey, privateKey); + if (mDHAgreePublicKey != null) + { + // For dss_fixed_dh and rsa_fixed_dh, the key arrived in the client certificate + return; + } + + BigInteger Yc = TlsDHUtilities.ReadDHParameter(input); + + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Yc, mDHParameters)); } - protected virtual AsymmetricCipherKeyPair GenerateDHKeyPair(DHParameters dhParams) + public override byte[] GeneratePremasterSecret() { - return TlsDHUtilities.GenerateDHKeyPair(context.SecureRandom, dhParams); + if (mAgreementCredentials != null) + { + return mAgreementCredentials.GenerateAgreement(mDHAgreePublicKey); + } + + if (mDHAgreePrivateKey != null) + { + return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreePublicKey, mDHAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); } - protected virtual void GenerateEphemeralClientKeyExchange(DHParameters dhParams, Stream output) + protected virtual int MinimumPrimeBits { - this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange( - context.SecureRandom, dhParams, output); + get { return 1024; } } - protected virtual DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key) + protected virtual DHParameters ValidateDHParameters(DHParameters parameters) { - return TlsDHUtilities.ValidateDHPublicKey(key); + if (parameters.P.BitLength < MinimumPrimeBits) + throw new TlsFatalAlert(AlertDescription.insufficient_security); + + return TlsDHUtilities.ValidateDHParameters(parameters); } } } diff --git a/crypto/src/crypto/tls/TlsDHUtilities.cs b/crypto/src/crypto/tls/TlsDHUtilities.cs
index b5deb8b84..727587135 100644 --- a/crypto/src/crypto/tls/TlsDHUtilities.cs +++ b/crypto/src/crypto/tls/TlsDHUtilities.cs
@@ -1,4 +1,5 @@ using System; +using System.Collections; using System.IO; using Org.BouncyCastle.Crypto.Agreement; @@ -7,11 +8,391 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Crypto.Tls { public abstract class TlsDHUtilities { + internal static readonly BigInteger Two = BigInteger.Two; + + /* + * TODO[draft-ietf-tls-negotiated-ff-dhe-01] Move these groups to DHStandardGroups once reaches RFC + */ + private static BigInteger FromHex(String hex) + { + return new BigInteger(1, Hex.Decode(hex)); + } + + private static DHParameters FromSafeP(String hexP) + { + BigInteger p = FromHex(hexP), q = p.ShiftRight(1); + return new DHParameters(p, Two, q); + } + + private static readonly string draft_ffdhe2432_p = + "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE13098533C8B3FFFFFFFFFFFFFFFF"; + internal static readonly DHParameters draft_ffdhe2432 = FromSafeP(draft_ffdhe2432_p); + + private static readonly string draft_ffdhe3072_p = + "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" + + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" + + "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF"; + internal static readonly DHParameters draft_ffdhe3072 = FromSafeP(draft_ffdhe3072_p); + + private static readonly string draft_ffdhe4096_p = + "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" + + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" + + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" + + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + + "A907600A918130C46DC778F971AD0038092999A333CB8B7A" + + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A" + + "FFFFFFFFFFFFFFFF"; + internal static readonly DHParameters draft_ffdhe4096 = FromSafeP(draft_ffdhe4096_p); + + private static readonly string draft_ffdhe6144_p = + "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" + + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" + + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" + + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + + "A907600A918130C46DC778F971AD0038092999A333CB8B7A" + + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" + + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" + + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" + + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" + + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" + + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" + + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" + + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" + + "D72B03746AE77F5E62292C311562A846505DC82DB854338A" + + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" + + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" + + "A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF"; + internal static readonly DHParameters draft_ffdhe6144 = FromSafeP(draft_ffdhe6144_p); + + private static readonly string draft_ffdhe8192_p = + "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" + + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" + + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" + + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + + "A907600A918130C46DC778F971AD0038092999A333CB8B7A" + + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" + + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" + + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" + + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" + + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" + + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" + + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" + + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" + + "D72B03746AE77F5E62292C311562A846505DC82DB854338A" + + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" + + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" + + "A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838" + + "1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E" + + "0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665" + + "CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282" + + "2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022" + + "BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C" + + "51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9" + + "D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457" + + "1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30" + + "FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D" + + "97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C" + + "D68C8BB7C5C6424CFFFFFFFFFFFFFFFF"; + internal static readonly DHParameters draft_ffdhe8192 = FromSafeP(draft_ffdhe8192_p); + + + public static void AddNegotiatedDheGroupsClientExtension(IDictionary extensions, byte[] dheGroups) + { + extensions[ExtensionType.negotiated_ff_dhe_groups] = CreateNegotiatedDheGroupsClientExtension(dheGroups); + } + + public static void AddNegotiatedDheGroupsServerExtension(IDictionary extensions, byte dheGroup) + { + extensions[ExtensionType.negotiated_ff_dhe_groups] = CreateNegotiatedDheGroupsServerExtension(dheGroup); + } + + public static byte[] GetNegotiatedDheGroupsClientExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.negotiated_ff_dhe_groups); + return extensionData == null ? null : ReadNegotiatedDheGroupsClientExtension(extensionData); + } + + public static short GetNegotiatedDheGroupsServerExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.negotiated_ff_dhe_groups); + return extensionData == null ? (short)-1 : (short)ReadNegotiatedDheGroupsServerExtension(extensionData); + } + + public static byte[] CreateNegotiatedDheGroupsClientExtension(byte[] dheGroups) + { + if (dheGroups == null || dheGroups.Length < 1 || dheGroups.Length > 255) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeUint8ArrayWithUint8Length(dheGroups); + } + + public static byte[] CreateNegotiatedDheGroupsServerExtension(byte dheGroup) + { + return new byte[]{ dheGroup }; + } + + public static byte[] ReadNegotiatedDheGroupsClientExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + byte length = TlsUtilities.ReadUint8(buf); + if (length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] dheGroups = TlsUtilities.ReadUint8Array(length, buf); + + TlsProtocol.AssertEmpty(buf); + + return dheGroups; + } + + public static byte ReadNegotiatedDheGroupsServerExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + if (extensionData.Length != 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + return extensionData[0]; + } + + public static DHParameters GetParametersForDHEGroup(short dheGroup) + { + switch (dheGroup) + { + case FiniteFieldDheGroup.ffdhe2432: + return draft_ffdhe2432; + case FiniteFieldDheGroup.ffdhe3072: + return draft_ffdhe3072; + case FiniteFieldDheGroup.ffdhe4096: + return draft_ffdhe4096; + case FiniteFieldDheGroup.ffdhe6144: + return draft_ffdhe6144; + case FiniteFieldDheGroup.ffdhe8192: + return draft_ffdhe8192; + default: + return null; + } + } + + public static bool ContainsDheCipherSuites(int[] cipherSuites) + { + for (int i = 0; i < cipherSuites.Length; ++i) + { + if (IsDheCipherSuite(cipherSuites[i])) + return true; + } + return false; + } + + public static bool IsDheCipherSuite(int cipherSuite) + { + switch (cipherSuite) + { + /* + * RFC 2246 + */ + case CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + + /* + * RFC 3268 + */ + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + + /* + * RFC 5932 + */ + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + + /* + * RFC 4162 + */ + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + + /* + * RFC 4279 + */ + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + + /* + * RFC 4785 + */ + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + + /* + * RFC 5246 + */ + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + + /* + * RFC 5288 + */ + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + + /* + * RFC 5487 + */ + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + + /* + * RFC 6367 + */ + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + + /* + * RFC 6655 + */ + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + + /* + * draft-josefsson-salsa20-tls-04 + */ + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + + return true; + + default: + return false; + } + } + + public static bool AreCompatibleParameters(DHParameters a, DHParameters b) + { + return a.P.Equals(b.P) && a.G.Equals(b.G); + } + public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey) { @@ -36,40 +417,60 @@ namespace Org.BouncyCastle.Crypto.Tls public static DHPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, DHParameters dhParams, Stream output) { - AsymmetricCipherKeyPair dhAgreeClientKeyPair = GenerateDHKeyPair(random, dhParams); - DHPrivateKeyParameters dhAgreeClientPrivateKey = - (DHPrivateKeyParameters)dhAgreeClientKeyPair.Private; + AsymmetricCipherKeyPair kp = GenerateDHKeyPair(random, dhParams); - BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.Public).Y; - byte[] keData = BigIntegers.AsUnsignedByteArray(Yc); - TlsUtilities.WriteOpaque16(keData, output); + DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.Public; + WriteDHParameter(dhPublic.Y, output); - return dhAgreeClientPrivateKey; + return (DHPrivateKeyParameters)kp.Private; } - - public static DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key) + + public static DHPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, + DHParameters dhParams, Stream output) + { + AsymmetricCipherKeyPair kp = GenerateDHKeyPair(random, dhParams); + + DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.Public; + new ServerDHParams(dhPublic).Encode(output); + + return (DHPrivateKeyParameters)kp.Private; + } + + public static DHParameters ValidateDHParameters(DHParameters parameters) { - BigInteger Y = key.Y; - DHParameters parameters = key.Parameters; BigInteger p = parameters.P; BigInteger g = parameters.G; if (!p.IsProbablePrime(2)) - { throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } - if (g.CompareTo(BigInteger.Two) < 0 || g.CompareTo(p.Subtract(BigInteger.Two)) > 0) - { + if (g.CompareTo(Two) < 0 || g.CompareTo(p.Subtract(Two)) > 0) throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } - if (Y.CompareTo(BigInteger.Two) < 0 || Y.CompareTo(p.Subtract(BigInteger.One)) > 0) - { + + + return parameters; + } + + public static DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key) + { + DHParameters parameters = ValidateDHParameters(key.Parameters); + + BigInteger Y = key.Y; + if (Y.CompareTo(Two) < 0 || Y.CompareTo(parameters.P.Subtract(Two)) > 0) throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } // TODO See RFC 2631 for more discussion of Diffie-Hellman validation return key; } + + public static BigInteger ReadDHParameter(Stream input) + { + return new BigInteger(1, TlsUtilities.ReadOpaque16(input)); + } + + public static void WriteDHParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output); + } } -} \ No newline at end of file +} diff --git a/crypto/src/crypto/tls/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
index ee6d6eb44..9831e8cd7 100644 --- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -1,53 +1,91 @@ using System; +using System.Collections; using System.IO; -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.Crypto.Tls { - internal class TlsDheKeyExchange - : TlsDHKeyExchange + public class TlsDheKeyExchange + : TlsDHKeyExchange { - internal TlsDheKeyExchange(TlsClientContext context, int keyExchange) - : base(context, keyExchange) + protected TlsSignerCredentials mServerCredentials = null; + + public TlsDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters) + : base(keyExchange, supportedSignatureAlgorithms, dhParameters) { } - public override void SkipServerKeyExchange() + public override void ProcessServerCredentials(TlsCredentials serverCredentials) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (!(serverCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsSignerCredentials)serverCredentials; + } + + public override byte[] GenerateServerKeyExchange() + { + if (this.mDHParameters == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + DigestInputBuffer buf = new DigestInputBuffer(); + + this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom, + this.mDHParameters, buf); + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm( + mContext, mServerCredentials); + + IDigest d = TlsUtilities.CreateHash(signatureAndHashAlgorithm); + + SecurityParameters securityParameters = mContext.SecurityParameters; + d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + buf.UpdateDigest(d); + + byte[] hash = DigestUtilities.DoFinal(d); + + byte[] signature = mServerCredentials.GenerateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.Encode(buf); + + return buf.ToArray(); } public override void ProcessServerKeyExchange(Stream input) { - SecurityParameters securityParameters = context.SecurityParameters; + SecurityParameters securityParameters = mContext.SecurityParameters; - ISigner signer = InitSigner(tlsSigner, securityParameters); - Stream sigIn = new SignerStream(input, signer, null); + SignerInputBuffer buf = new SignerInputBuffer(); + Stream teeIn = new TeeInputStream(input, buf); - byte[] pBytes = TlsUtilities.ReadOpaque16(sigIn); - byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn); - byte[] YsBytes = TlsUtilities.ReadOpaque16(sigIn); + ServerDHParams dhParams = ServerDHParams.Parse(teeIn); - byte[] sigByte = TlsUtilities.ReadOpaque16(input); - if (!signer.VerifySignature(sigByte)) - { - throw new TlsFatalAlert(AlertDescription.decrypt_error); - } + DigitallySigned signed_params = DigitallySigned.Parse(mContext, input); - BigInteger p = new BigInteger(1, pBytes); - BigInteger g = new BigInteger(1, gBytes); - BigInteger Ys = new BigInteger(1, YsBytes); + ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters); + buf.UpdateSigner(signer); + if (!signer.VerifySignature(signed_params.Signature)) + throw new TlsFatalAlert(AlertDescription.decrypt_error); - this.dhAgreeServerPublicKey = ValidateDHPublicKey( - new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(dhParams.PublicKey); + this.mDHParameters = ValidateDHParameters(mDHAgreePublicKey.Parameters); } - protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters) + protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, + SecurityParameters securityParameters) { - ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey); signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); return signer; diff --git a/crypto/src/crypto/tls/TlsDsaSigner.cs b/crypto/src/crypto/tls/TlsDsaSigner.cs
index bba114e90..f0c1e9451 100644 --- a/crypto/src/crypto/tls/TlsDsaSigner.cs +++ b/crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -7,45 +7,76 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - internal abstract class TlsDsaSigner - : TlsSigner + public abstract class TlsDsaSigner + : AbstractTlsSigner { - public virtual byte[] GenerateRawSignature(SecureRandom random, - AsymmetricKeyParameter privateKey, byte[] md5andsha1) + public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) { - ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random)); - // Note: Only use the SHA1 part of the hash - s.BlockUpdate(md5andsha1, 16, 20); - return s.GenerateSignature(); + ISigner signer = MakeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.BlockUpdate(hash, 16, 20); + } + else + { + signer.BlockUpdate(hash, 0, hash.Length); + } + return signer.GenerateSignature(); } - public bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1) + public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) { - ISigner s = MakeSigner(new NullDigest(), false, publicKey); - // Note: Only use the SHA1 part of the hash - s.BlockUpdate(md5andsha1, 16, 20); - return s.VerifySignature(sigBytes); + ISigner signer = MakeSigner(algorithm, true, false, publicKey); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.BlockUpdate(hash, 16, 20); + } + else + { + signer.BlockUpdate(hash, 0, hash.Length); + } + return signer.VerifySignature(sigBytes); } - public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey) + public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) { - return MakeSigner(new Sha1Digest(), true, new ParametersWithRandom(privateKey, random)); + return MakeSigner(algorithm, false, true, privateKey); } - public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) { - return MakeSigner(new Sha1Digest(), false, publicKey); + return MakeSigner(algorithm, false, false, publicKey); } - public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + protected virtual ICipherParameters MakeInitParameters(bool forSigning, ICipherParameters cp) + { + return cp; + } - protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp) + protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning, + ICipherParameters cp) { - ISigner s = new DsaDigestSigner(CreateDsaImpl(), d); - s.Init(forSigning, cp); + if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext)) + throw new InvalidOperationException(); + + if (algorithm != null && algorithm.Signature != SignatureAlgorithm) + throw new InvalidOperationException(); + + byte hashAlgorithm = algorithm == null ? HashAlgorithm.sha1 : algorithm.Hash; + IDigest d = raw ? new NullDigest() : TlsUtilities.CreateHash(hashAlgorithm); + + ISigner s = new DsaDigestSigner(CreateDsaImpl(hashAlgorithm), d); + s.Init(forSigning, MakeInitParameters(forSigning, cp)); return s; } - protected abstract IDsa CreateDsaImpl(); + protected abstract byte SignatureAlgorithm { get; } + + protected abstract IDsa CreateDsaImpl(byte hashAlgorithm); } } diff --git a/crypto/src/crypto/tls/TlsDssSigner.cs b/crypto/src/crypto/tls/TlsDssSigner.cs
index c6f1abcec..707ef3853 100644 --- a/crypto/src/crypto/tls/TlsDssSigner.cs +++ b/crypto/src/crypto/tls/TlsDssSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsDssSigner - : TlsDsaSigner - { - public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) - { - return publicKey is DsaPublicKeyParameters; - } + public class TlsDssSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is DsaPublicKeyParameters; + } - protected override IDsa CreateDsaImpl() - { - return new DsaSigner(); - } - } + protected override IDsa CreateDsaImpl(byte hashAlgorithm) + { + return new DsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm))); + } + + protected override byte SignatureAlgorithm + { + get { return Tls.SignatureAlgorithm.dsa; } + } + } } diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
index 65d07a10c..992be4aca 100644 --- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -3,149 +3,154 @@ using System.Collections; using System.IO; using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Crypto.Agreement; -using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - /** - * ECDH key exchange (see RFC 4492) - */ - internal class TlsECDHKeyExchange - : TlsKeyExchange + /// <summary>(D)TLS ECDH key exchange (see RFC 4492).</summary> + public class TlsECDHKeyExchange + : AbstractTlsKeyExchange { - protected TlsClientContext context; - protected int keyExchange; - protected TlsSigner tlsSigner; + protected TlsSigner mTlsSigner; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; - protected AsymmetricKeyParameter serverPublicKey; - protected ECPublicKeyParameters ecAgreeServerPublicKey; - protected TlsAgreementCredentials agreementCredentials; - protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null; + protected AsymmetricKeyParameter mServerPublicKey; + protected TlsAgreementCredentials mAgreementCredentials; - internal TlsECDHKeyExchange(TlsClientContext context, int keyExchange) + protected ECPrivateKeyParameters mECAgreePrivateKey; + protected ECPublicKeyParameters mECAgreePublicKey; + + public TlsECDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves, + byte[] clientECPointFormats, byte[] serverECPointFormats) + : base(keyExchange, supportedSignatureAlgorithms) { switch (keyExchange) { - case KeyExchangeAlgorithm.ECDHE_RSA: - this.tlsSigner = new TlsRsaSigner(); - break; - case KeyExchangeAlgorithm.ECDHE_ECDSA: - this.tlsSigner = new TlsECDsaSigner(); - break; - case KeyExchangeAlgorithm.ECDH_RSA: - case KeyExchangeAlgorithm.ECDH_ECDSA: - this.tlsSigner = null; - break; - default: - throw new ArgumentException("unsupported key exchange algorithm", "keyExchange"); + case KeyExchangeAlgorithm.ECDHE_RSA: + this.mTlsSigner = new TlsRsaSigner(); + break; + case KeyExchangeAlgorithm.ECDHE_ECDSA: + this.mTlsSigner = new TlsECDsaSigner(); + break; + case KeyExchangeAlgorithm.ECDH_RSA: + case KeyExchangeAlgorithm.ECDH_ECDSA: + this.mTlsSigner = null; + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); } - this.context = context; - this.keyExchange = keyExchange; + this.mNamedCurves = namedCurves; + this.mClientECPointFormats = clientECPointFormats; + this.mServerECPointFormats = serverECPointFormats; } - public virtual void SkipServerCertificate() + public override void Init(TlsContext context) + { + base.Init(context); + + if (this.mTlsSigner != null) + { + this.mTlsSigner.Init(context); + } + } + + public override void SkipServerCredentials() { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public virtual void ProcessServerCertificate(Certificate serverCertificate) + public override void ProcessServerCertificate(Certificate serverCertificate) { + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); - SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; try { - this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); } - catch (Exception) + catch (Exception e) { - throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); } - if (tlsSigner == null) + if (mTlsSigner == null) { try { - this.ecAgreeServerPublicKey = ValidateECPublicKey((ECPublicKeyParameters)this.serverPublicKey); + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey((ECPublicKeyParameters) this.mServerPublicKey); } - catch (InvalidCastException) + catch (InvalidCastException e) { - throw new TlsFatalAlert(AlertDescription.certificate_unknown); + throw new TlsFatalAlert(AlertDescription.certificate_unknown, e); } TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement); } else { - if (!tlsSigner.IsValidPublicKey(this.serverPublicKey)) - { + if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey)) throw new TlsFatalAlert(AlertDescription.certificate_unknown); - } TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); } - - // TODO - /* - * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the - * signing algorithm for the certificate must be the same as the algorithm for the - * certificate key." - */ - } - - public virtual void SkipServerKeyExchange() - { - // do nothing + + base.ProcessServerCertificate(serverCertificate); } - public virtual void ProcessServerKeyExchange(Stream input) + public override bool RequiresServerKeyExchange { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + get + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.ECDHE_ECDSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + case KeyExchangeAlgorithm.ECDH_anon: + return true; + default: + return false; + } + } } - public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { /* - * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable - * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is - * prohibited because the use of a long-term ECDH client key would jeopardize the - * forward secrecy property of these algorithms. + * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with + * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because + * the use of a long-term ECDH client key would jeopardize the forward secrecy property of + * these algorithms. */ byte[] types = certificateRequest.CertificateTypes; - foreach (byte type in types) + for (int i = 0; i < types.Length; ++i) { - switch (type) + switch (types[i]) { - case ClientCertificateType.rsa_sign: - case ClientCertificateType.dss_sign: - case ClientCertificateType.ecdsa_sign: - case ClientCertificateType.rsa_fixed_ecdh: - case ClientCertificateType.ecdsa_fixed_ecdh: - break; - default: - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + case ClientCertificateType.rsa_fixed_ecdh: + case ClientCertificateType.ecdsa_fixed_ecdh: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } - public virtual void SkipClientCredentials() - { - this.agreementCredentials = null; - } - - public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { if (clientCredentials is TlsAgreementCredentials) { - // TODO Validate client cert has matching parameters (see 'AreOnSameCurve')? + // TODO Validate client cert has matching parameters (see 'TlsEccUtilities.AreOnSameCurve')? - this.agreementCredentials = (TlsAgreementCredentials)clientCredentials; + this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials; } else if (clientCredentials is TlsSignerCredentials) { @@ -157,80 +162,50 @@ namespace Org.BouncyCastle.Crypto.Tls } } - public virtual void GenerateClientKeyExchange(Stream output) + public override void GenerateClientKeyExchange(Stream output) { - if (agreementCredentials == null) + if (mAgreementCredentials == null) { - GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output); + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom, + mServerECPointFormats, mECAgreePublicKey.Parameters, output); } } - public virtual byte[] GeneratePremasterSecret() + public override void ProcessClientCertificate(Certificate clientCertificate) { - if (agreementCredentials != null) - { - return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey); - } - - return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey); - } - - protected virtual bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b) - { - // TODO Move to ECDomainParameters.Equals() or other utility method? - return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H); + // TODO Extract the public key + // TODO If the certificate is 'fixed', take the public key as mECAgreeClientPublicKey } - protected virtual byte[] ExternalizeKey(ECPublicKeyParameters keyParameters) + public override void ProcessClientKeyExchange(Stream input) { - // TODO Add support for compressed encoding and SPF extension - - /* - * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. - * Here, the format MUST conform to what the server has requested through a - * Supported Point Formats Extension if this extension was used, and MUST be - * uncompressed if this extension was not used. - */ - return keyParameters.Q.GetEncoded(); - } + if (mECAgreePublicKey != null) + { + // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate + return; + } - protected virtual AsymmetricCipherKeyPair GenerateECKeyPair(ECDomainParameters ecParams) - { - ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); - ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams, - context.SecureRandom); - keyPairGenerator.Init(keyGenerationParameters); - return keyPairGenerator.GenerateKeyPair(); - } + byte[] point = TlsUtilities.ReadOpaque8(input); - protected virtual void GenerateEphemeralClientKeyExchange(ECDomainParameters ecParams, Stream output) - { - AsymmetricCipherKeyPair ecAgreeClientKeyPair = GenerateECKeyPair(ecParams); - this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.Private; + ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters; - byte[] keData = ExternalizeKey((ECPublicKeyParameters)ecAgreeClientKeyPair.Public); - TlsUtilities.WriteOpaque8(keData, output); + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mServerECPointFormats, curve_params, point)); } - protected virtual byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, - ECPrivateKeyParameters privateKey) + public override byte[] GeneratePremasterSecret() { - ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); - basicAgreement.Init(privateKey); - BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey); + if (mAgreementCredentials != null) + { + return mAgreementCredentials.GenerateAgreement(mECAgreePublicKey); + } - /* - * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by - * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for - * any given field; leading zeros found in this octet string MUST NOT be truncated. - */ - return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue); - } + if (mECAgreePrivateKey != null) + { + return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey); + } - protected virtual ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) - { - // TODO Check RFC 4492 for validation - return key; + throw new TlsFatalAlert(AlertDescription.internal_error); } } } diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index a671ebfbe..b681aada3 100644 --- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -2,91 +2,111 @@ using System; using System.Collections; using System.IO; -using Org.BouncyCastle.Crypto.IO; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { - /** - * ECDHE key exchange (see RFC 4492) - */ - internal class TlsECDheKeyExchange : TlsECDHKeyExchange + /// <summary>(D)TLS ECDHE key exchange (see RFC 4492).</summary> + public class TlsECDheKeyExchange + : TlsECDHKeyExchange { - internal TlsECDheKeyExchange(TlsClientContext context, int keyExchange) - : base(context, keyExchange) + protected TlsSignerCredentials mServerCredentials = null; + + public TlsECDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves, + byte[] clientECPointFormats, byte[] serverECPointFormats) + : base(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats) { } - public override void SkipServerKeyExchange() + public override void ProcessServerCredentials(TlsCredentials serverCredentials) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (!(serverCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsSignerCredentials)serverCredentials; } - public override void ProcessServerKeyExchange(Stream input) + public override byte[] GenerateServerKeyExchange() { - SecurityParameters securityParameters = context.SecurityParameters; + DigestInputBuffer buf = new DigestInputBuffer(); - ISigner signer = InitSigner(tlsSigner, securityParameters); - Stream sigIn = new SignerStream(input, signer, null); + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom, mNamedCurves, + mClientECPointFormats, buf); - byte curveType = TlsUtilities.ReadUint8(sigIn); - ECDomainParameters curve_params; + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm( + mContext, mServerCredentials); - // Currently, we only support named curves - if (curveType == ECCurveType.named_curve) - { - int namedCurve = TlsUtilities.ReadUint16(sigIn); + IDigest d = TlsUtilities.CreateHash(signatureAndHashAlgorithm); - // TODO Check namedCurve is one we offered? + SecurityParameters securityParameters = mContext.SecurityParameters; + d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + buf.UpdateDigest(d); - curve_params = NamedCurveHelper.GetECParameters(namedCurve); - } - else - { - // TODO Add support for explicit curve parameters (read from sigIn) + byte[] hash = DigestUtilities.DoFinal(d); - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } + byte[] signature = mServerCredentials.GenerateCertificateSignature(hash); - byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn); + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.Encode(buf); - byte[] sigByte = TlsUtilities.ReadOpaque16(input); - if (!signer.VerifySignature(sigByte)) - { - throw new TlsFatalAlert(AlertDescription.decrypt_error); - } + return buf.ToArray(); + } + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = mContext.SecurityParameters; - // TODO Check curve_params not null + SignerInputBuffer buf = new SignerInputBuffer(); + Stream teeIn = new TeeInputStream(input, buf); - ECPoint Q = curve_params.Curve.DecodePoint(publicBytes); + ECDomainParameters curve_params = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, teeIn); - this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params)); + byte[] point = TlsUtilities.ReadOpaque8(teeIn); + + DigitallySigned signed_params = DigitallySigned.Parse(mContext, input); + + ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters); + buf.UpdateSigner(signer); + if (!signer.VerifySignature(signed_params.Signature)) + throw new TlsFatalAlert(AlertDescription.decrypt_error); + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mClientECPointFormats, curve_params, point)); } - + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { /* - * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable - * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is - * prohibited because the use of a long-term ECDH client key would jeopardize the - * forward secrecy property of these algorithms. + * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with + * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because + * the use of a long-term ECDH client key would jeopardize the forward secrecy property of + * these algorithms. */ byte[] types = certificateRequest.CertificateTypes; - foreach (byte type in types) + for (int i = 0; i < types.Length; ++i) { - switch (type) + switch (types[i]) { - case ClientCertificateType.rsa_sign: - case ClientCertificateType.dss_sign: - case ClientCertificateType.ecdsa_sign: - break; - default: - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } - + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { if (clientCredentials is TlsSignerCredentials) @@ -99,9 +119,10 @@ namespace Org.BouncyCastle.Crypto.Tls } } - protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters) + protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, + SecurityParameters securityParameters) { - ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey); signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); return signer; diff --git a/crypto/src/crypto/tls/TlsECDsaSigner.cs b/crypto/src/crypto/tls/TlsECDsaSigner.cs
index 3c30fdc0c..fa9d0b714 100644 --- a/crypto/src/crypto/tls/TlsECDsaSigner.cs +++ b/crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsECDsaSigner - : TlsDsaSigner - { - public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) - { - return publicKey is ECPublicKeyParameters; - } + public class TlsECDsaSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is ECPublicKeyParameters; + } - protected override IDsa CreateDsaImpl() - { - return new ECDsaSigner(); - } - } + protected override IDsa CreateDsaImpl(byte hashAlgorithm) + { + return new ECDsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm))); + } + + protected override byte SignatureAlgorithm + { + get { return Tls.SignatureAlgorithm.ecdsa; } + } + } } diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs new file mode 100644
index 000000000..d0d794d0e --- /dev/null +++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -0,0 +1,718 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.EC; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.Field; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsEccUtilities + { + private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1", + "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", + "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1", + "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1", + "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"}; + + public static void AddSupportedEllipticCurvesExtension(IDictionary extensions, int[] namedCurves) + { + extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves); + } + + public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats) + { + extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats); + } + + public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves); + return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData); + } + + public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats); + return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData); + } + + public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves) + { + if (namedCurves == null || namedCurves.Length < 1) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves); + } + + public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats) + { + if (ecPointFormats == null || !Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + + // NOTE: We add it at the end (lowest preference) + ecPointFormats = Arrays.Append(ecPointFormats, ECPointFormat.uncompressed); + } + + return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats); + } + + public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + int length = TlsUtilities.ReadUint16(buf); + if (length < 2 || (length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf); + + TlsProtocol.AssertEmpty(buf); + + return namedCurves; + } + + public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + byte length = TlsUtilities.ReadUint8(buf); + if (length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] ecPointFormats = TlsUtilities.ReadUint8Array(length, buf); + + TlsProtocol.AssertEmpty(buf); + + if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return ecPointFormats; + } + + public static string GetNameOfNamedCurve(int namedCurve) + { + return IsSupportedNamedCurve(namedCurve) ? CurveNames[namedCurve - 1] : null; + } + + public static ECDomainParameters GetParametersForNamedCurve(int namedCurve) + { + string curveName = GetNameOfNamedCurve(namedCurve); + if (curveName == null) + return null; + + // Parameters are lazily created the first time a particular curve is accessed + + X9ECParameters ecP = CustomNamedCurves.GetByName(curveName); + if (ecP == null) + { + ecP = ECNamedCurveTable.GetByName(curveName); + if (ecP == null) + return null; + } + + // It's a bit inefficient to do this conversion every time + return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + } + + public static bool HasAnySupportedNamedCurves() + { + return CurveNames.Length > 0; + } + + public static bool ContainsEccCipherSuites(int[] cipherSuites) + { + for (int i = 0; i < cipherSuites.Length; ++i) + { + if (IsEccCipherSuite(cipherSuites[i])) + return true; + } + return false; + } + + public static bool IsEccCipherSuite(int cipherSuite) + { + switch (cipherSuite) + { + /* + * RFC 4492 + */ + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA: + + /* + * RFC 5289 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + + /* + * RFC 5489 + */ + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + + /* + * RFC 6367 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + + /* + * RFC 7251 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + + /* + * draft-josefsson-salsa20-tls-04 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + + return true; + + default: + return false; + } + } + + public static bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b) + { + // TODO Move to ECDomainParameters.Equals() or other utility method? + return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H); + } + + public static bool IsSupportedNamedCurve(int namedCurve) + { + return (namedCurve > 0 && namedCurve <= CurveNames.Length); + } + + public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat) + { + if (ecPointFormats == null) + return false; + + for (int i = 0; i < ecPointFormats.Length; ++i) + { + byte ecPointFormat = ecPointFormats[i]; + if (ecPointFormat == ECPointFormat.uncompressed) + return false; + if (ecPointFormat == compressionFormat) + return true; + } + return false; + } + + public static byte[] SerializeECFieldElement(int fieldSize, BigInteger x) + { + return BigIntegers.AsUnsignedByteArray((fieldSize + 7) / 8, x); + } + + public static byte[] SerializeECPoint(byte[] ecPointFormats, ECPoint point) + { + ECCurve curve = point.Curve; + + /* + * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the + * format MUST conform to what the server has requested through a Supported Point Formats + * Extension if this extension was used, and MUST be uncompressed if this extension was not + * used. + */ + bool compressed = false; + if (ECAlgorithms.IsFpCurve(curve)) + { + compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2); + } + return point.GetEncoded(compressed); + } + + public static byte[] SerializeECPublicKey(byte[] ecPointFormats, ECPublicKeyParameters keyParameters) + { + return SerializeECPoint(ecPointFormats, keyParameters.Q); + } + + public static BigInteger DeserializeECFieldElement(int fieldSize, byte[] encoding) + { + int requiredLength = (fieldSize + 7) / 8; + if (encoding.Length != requiredLength) + throw new TlsFatalAlert(AlertDescription.decode_error); + return new BigInteger(1, encoding); + } + + public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding) + { + if (encoding == null || encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + byte actualFormat; + switch (encoding[0]) + { + case 0x02: // compressed + case 0x03: // compressed + { + if (ECAlgorithms.IsF2mCurve(curve)) + { + actualFormat = ECPointFormat.ansiX962_compressed_char2; + } + else if (ECAlgorithms.IsFpCurve(curve)) + { + actualFormat = ECPointFormat.ansiX962_compressed_prime; + } + else + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + break; + } + case 0x04: // uncompressed + { + actualFormat = ECPointFormat.uncompressed; + break; + } + case 0x00: // infinity + case 0x06: // hybrid + case 0x07: // hybrid + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + if (actualFormat != ECPointFormat.uncompressed + && (ecPointFormats == null || !Arrays.Contains(ecPointFormats, actualFormat))) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return curve.DecodePoint(encoding); + } + + public static ECPublicKeyParameters DeserializeECPublicKey(byte[] ecPointFormats, ECDomainParameters curve_params, + byte[] encoding) + { + try + { + ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding); + return new ECPublicKeyParameters(Y, curve_params); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + } + + public static byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) + { + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.Init(privateKey); + BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey); + + /* + * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by + * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for + * any given field; leading zeros found in this octet string MUST NOT be truncated. + */ + return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue); + } + + public static AsymmetricCipherKeyPair GenerateECKeyPair(SecureRandom random, ECDomainParameters ecParams) + { + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + keyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, random)); + return keyPairGenerator.GenerateKeyPair(); + } + + public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats, + ECDomainParameters ecParams, Stream output) + { + AsymmetricCipherKeyPair kp = GenerateECKeyPair(random, ecParams); + + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public; + WriteECPoint(ecPointFormats, ecPublicKey.Q, output); + + return (ECPrivateKeyParameters)kp.Private; + } + + // TODO Refactor around ServerECDHParams before making this public + internal static ECPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves, + byte[] ecPointFormats, Stream output) + { + /* First we try to find a supported named curve from the client's list. */ + int namedCurve = -1; + if (namedCurves == null) + { + // TODO Let the peer choose the default named curve + namedCurve = NamedCurve.secp256r1; + } + else + { + for (int i = 0; i < namedCurves.Length; ++i) + { + int entry = namedCurves[i]; + if (NamedCurve.IsValid(entry) && IsSupportedNamedCurve(entry)) + { + namedCurve = entry; + break; + } + } + } + + ECDomainParameters ecParams = null; + if (namedCurve >= 0) + { + ecParams = GetParametersForNamedCurve(namedCurve); + } + else + { + /* If no named curves are suitable, check if the client supports explicit curves. */ + if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves)) + { + ecParams = GetParametersForNamedCurve(NamedCurve.secp256r1); + } + else if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves)) + { + ecParams = GetParametersForNamedCurve(NamedCurve.sect283r1); + } + } + + if (ecParams == null) + { + /* + * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find + * a suitable curve. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (namedCurve < 0) + { + WriteExplicitECParameters(ecPointFormats, ecParams, output); + } + else + { + WriteNamedECParameters(namedCurve, output); + } + + return GenerateEphemeralClientKeyExchange(random, ecPointFormats, ecParams, output); + } + + public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) + { + // TODO Check RFC 4492 for validation + return key; + } + + public static int ReadECExponent(int fieldSize, Stream input) + { + BigInteger K = ReadECParameter(input); + if (K.BitLength < 32) + { + int k = K.IntValue; + if (k > 0 && k < fieldSize) + { + return k; + } + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public static BigInteger ReadECFieldElement(int fieldSize, Stream input) + { + return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input)); + } + + public static BigInteger ReadECParameter(Stream input) + { + // TODO Are leading zeroes okay here? + return new BigInteger(1, TlsUtilities.ReadOpaque8(input)); + } + + public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input) + { + try + { + byte curveType = TlsUtilities.ReadUint8(input); + + switch (curveType) + { + case ECCurveType.explicit_prime: + { + CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves); + + BigInteger prime_p = ReadECParameter(input); + BigInteger a = ReadECFieldElement(prime_p.BitLength, input); + BigInteger b = ReadECFieldElement(prime_p.BitLength, input); + byte[] baseEncoding = TlsUtilities.ReadOpaque8(input); + BigInteger order = ReadECParameter(input); + BigInteger cofactor = ReadECParameter(input); + ECCurve curve = new FpCurve(prime_p, a, b, order, cofactor); + ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding); + return new ECDomainParameters(curve, basePoint, order, cofactor); + } + case ECCurveType.explicit_char2: + { + CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves); + + int m = TlsUtilities.ReadUint16(input); + byte basis = TlsUtilities.ReadUint8(input); + if (!ECBasisType.IsValid(basis)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + int k1 = ReadECExponent(m, input), k2 = -1, k3 = -1; + if (basis == ECBasisType.ec_basis_pentanomial) + { + k2 = ReadECExponent(m, input); + k3 = ReadECExponent(m, input); + } + + BigInteger a = ReadECFieldElement(m, input); + BigInteger b = ReadECFieldElement(m, input); + byte[] baseEncoding = TlsUtilities.ReadOpaque8(input); + BigInteger order = ReadECParameter(input); + BigInteger cofactor = ReadECParameter(input); + + ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial) + ? new F2mCurve(m, k1, k2, k3, a, b, order, cofactor) + : new F2mCurve(m, k1, a, b, order, cofactor); + + ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding); + + return new ECDomainParameters(curve, basePoint, order, cofactor); + } + case ECCurveType.named_curve: + { + int namedCurve = TlsUtilities.ReadUint16(input); + if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a + * specific curve. Values of NamedCurve that indicate support for a class of + * explicitly defined curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + CheckNamedCurve(namedCurves, namedCurve); + + return GetParametersForNamedCurve(namedCurve); + } + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + } + + private static void CheckNamedCurve(int[] namedCurves, int namedCurve) + { + if (namedCurves != null && !Arrays.Contains(namedCurves, namedCurve)) + { + /* + * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite + * unless they can complete the handshake while respecting the choice of curves + * and compression techniques specified by the client. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public static void WriteECExponent(int k, Stream output) + { + BigInteger K = BigInteger.ValueOf(k); + WriteECParameter(K, output); + } + + public static void WriteECFieldElement(ECFieldElement x, Stream output) + { + TlsUtilities.WriteOpaque8(x.GetEncoded(), output); + } + + public static void WriteECFieldElement(int fieldSize, BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque8(SerializeECFieldElement(fieldSize, x), output); + } + + public static void WriteECParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque8(BigIntegers.AsUnsignedByteArray(x), output); + } + + public static void WriteExplicitECParameters(byte[] ecPointFormats, ECDomainParameters ecParameters, + Stream output) + { + ECCurve curve = ecParameters.Curve; + + if (ECAlgorithms.IsFpCurve(curve)) + { + TlsUtilities.WriteUint8(ECCurveType.explicit_prime, output); + + WriteECParameter(curve.Field.Characteristic, output); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field; + int[] exponents = field.MinimalPolynomial.GetExponentsPresent(); + + TlsUtilities.WriteUint8(ECCurveType.explicit_char2, output); + + int m = exponents[exponents.Length - 1]; + TlsUtilities.CheckUint16(m); + TlsUtilities.WriteUint16(m, output); + + if (exponents.Length == 3) + { + TlsUtilities.WriteUint8(ECBasisType.ec_basis_trinomial, output); + WriteECExponent(exponents[1], output); + } + else if (exponents.Length == 5) + { + TlsUtilities.WriteUint8(ECBasisType.ec_basis_pentanomial, output); + WriteECExponent(exponents[1], output); + WriteECExponent(exponents[2], output); + WriteECExponent(exponents[3], output); + } + else + { + throw new ArgumentException("Only trinomial and pentomial curves are supported"); + } + } + else + { + throw new ArgumentException("'ecParameters' not a known curve type"); + } + + WriteECFieldElement(curve.A, output); + WriteECFieldElement(curve.B, output); + TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, ecParameters.G), output); + WriteECParameter(ecParameters.N, output); + WriteECParameter(ecParameters.H, output); + } + + public static void WriteECPoint(byte[] ecPointFormats, ECPoint point, Stream output) + { + TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, point), output); + } + + public static void WriteNamedECParameters(int namedCurve, Stream output) + { + if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific + * curve. Values of NamedCurve that indicate support for a class of explicitly defined + * curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + TlsUtilities.WriteUint8(ECCurveType.named_curve, output); + TlsUtilities.CheckUint16(namedCurve); + TlsUtilities.WriteUint16(namedCurve, output); + } + } +} diff --git a/crypto/src/crypto/tls/TlsEncryptionCredentials.cs b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs new file mode 100644
index 000000000..52f007006 --- /dev/null +++ b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsEncryptionCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret); + } +} diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs new file mode 100644
index 000000000..46851b66c --- /dev/null +++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
@@ -0,0 +1,258 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsExtensionsUtilities + { + public static IDictionary EnsureExtensionsInitialised(IDictionary extensions) + { + return extensions == null ? Platform.CreateHashtable() : extensions; + } + + public static void AddEncryptThenMacExtension(IDictionary extensions) + { + extensions[ExtensionType.encrypt_then_mac] = CreateEncryptThenMacExtension(); + } + + public static void AddExtendedMasterSecretExtension(IDictionary extensions) + { + extensions[ExtensionType.extended_master_secret] = CreateExtendedMasterSecretExtension(); + } + + /// <exception cref="IOException"></exception> + public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension) + { + extensions[ExtensionType.heartbeat] = CreateHeartbeatExtension(heartbeatExtension); + } + + /// <exception cref="IOException"></exception> + public static void AddMaxFragmentLengthExtension(IDictionary extensions, byte maxFragmentLength) + { + extensions[ExtensionType.max_fragment_length] = CreateMaxFragmentLengthExtension(maxFragmentLength); + } + + /// <exception cref="IOException"></exception> + public static void AddServerNameExtension(IDictionary extensions, ServerNameList serverNameList) + { + extensions[ExtensionType.server_name] = CreateServerNameExtension(serverNameList); + } + + /// <exception cref="IOException"></exception> + public static void AddStatusRequestExtension(IDictionary extensions, CertificateStatusRequest statusRequest) + { + extensions[ExtensionType.status_request] = CreateStatusRequestExtension(statusRequest); + } + + public static void AddTruncatedHMacExtension(IDictionary extensions) + { + extensions[ExtensionType.truncated_hmac] = CreateTruncatedHMacExtension(); + } + + /// <exception cref="IOException"></exception> + public static HeartbeatExtension GetHeartbeatExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.heartbeat); + return extensionData == null ? null : ReadHeartbeatExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static short GetMaxFragmentLengthExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.max_fragment_length); + return extensionData == null ? (short)-1 : (short)ReadMaxFragmentLengthExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static ServerNameList GetServerNameExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name); + return extensionData == null ? null : ReadServerNameExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static CertificateStatusRequest GetStatusRequestExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request); + return extensionData == null ? null : ReadStatusRequestExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static bool HasEncryptThenMacExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.encrypt_then_mac); + return extensionData == null ? false : ReadEncryptThenMacExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static bool HasExtendedMasterSecretExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.extended_master_secret); + return extensionData == null ? false : ReadExtendedMasterSecretExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static bool HasTruncatedHMacExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac); + return extensionData == null ? false : ReadTruncatedHMacExtension(extensionData); + } + + public static byte[] CreateEmptyExtensionData() + { + return TlsUtilities.EmptyBytes; + } + + public static byte[] CreateEncryptThenMacExtension() + { + return CreateEmptyExtensionData(); + } + + public static byte[] CreateExtendedMasterSecretExtension() + { + return CreateEmptyExtensionData(); + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension) + { + if (heartbeatExtension == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + MemoryStream buf = new MemoryStream(); + + heartbeatExtension.Encode(buf); + + return buf.ToArray(); + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateMaxFragmentLengthExtension(byte maxFragmentLength) + { + return new byte[]{ maxFragmentLength }; + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateServerNameExtension(ServerNameList serverNameList) + { + if (serverNameList == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + MemoryStream buf = new MemoryStream(); + + serverNameList.Encode(buf); + + return buf.ToArray(); + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateStatusRequestExtension(CertificateStatusRequest statusRequest) + { + if (statusRequest == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + MemoryStream buf = new MemoryStream(); + + statusRequest.Encode(buf); + + return buf.ToArray(); + } + + public static byte[] CreateTruncatedHMacExtension() + { + return CreateEmptyExtensionData(); + } + + /// <exception cref="IOException"></exception> + private static bool ReadEmptyExtensionData(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + if (extensionData.Length != 0) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return true; + } + + /// <exception cref="IOException"></exception> + public static bool ReadEncryptThenMacExtension(byte[] extensionData) + { + return ReadEmptyExtensionData(extensionData); + } + + /// <exception cref="IOException"></exception> + public static bool ReadExtendedMasterSecretExtension(byte[] extensionData) + { + return ReadEmptyExtensionData(extensionData); + } + + /// <exception cref="IOException"></exception> + public static HeartbeatExtension ReadHeartbeatExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + HeartbeatExtension heartbeatExtension = HeartbeatExtension.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + return heartbeatExtension; + } + + /// <exception cref="IOException"></exception> + public static short ReadMaxFragmentLengthExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + if (extensionData.Length != 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + return extensionData[0]; + } + + /// <exception cref="IOException"></exception> + public static ServerNameList ReadServerNameExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + ServerNameList serverNameList = ServerNameList.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + return serverNameList; + } + + /// <exception cref="IOException"></exception> + public static CertificateStatusRequest ReadStatusRequestExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + CertificateStatusRequest statusRequest = CertificateStatusRequest.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + return statusRequest; + } + + /// <exception cref="IOException"></exception> + public static bool ReadTruncatedHMacExtension(byte[] extensionData) + { + return ReadEmptyExtensionData(extensionData); + } + } +} diff --git a/crypto/src/crypto/tls/TlsFatalAlert.cs b/crypto/src/crypto/tls/TlsFatalAlert.cs
index 4fb2a41bd..55d784dd9 100644 --- a/crypto/src/crypto/tls/TlsFatalAlert.cs +++ b/crypto/src/crypto/tls/TlsFatalAlert.cs
@@ -9,6 +9,12 @@ namespace Org.BouncyCastle.Crypto.Tls private readonly byte alertDescription; public TlsFatalAlert(byte alertDescription) + : this(alertDescription, null) + { + } + + public TlsFatalAlert(byte alertDescription, Exception alertCause) + : base(Tls.AlertDescription.GetText(alertDescription), alertCause) { this.alertDescription = alertDescription; } diff --git a/crypto/src/crypto/tls/TlsHandshakeHash.cs b/crypto/src/crypto/tls/TlsHandshakeHash.cs new file mode 100644
index 000000000..7118d9769 --- /dev/null +++ b/crypto/src/crypto/tls/TlsHandshakeHash.cs
@@ -0,0 +1,22 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsHandshakeHash + : IDigest + { + void Init(TlsContext context); + + TlsHandshakeHash NotifyPrfDetermined(); + + void TrackHashAlgorithm(byte hashAlgorithm); + + void SealHashAlgorithms(); + + TlsHandshakeHash StopTracking(); + + IDigest ForkPrfHash(); + + byte[] GetFinalHash(byte hashAlgorithm); + } +} diff --git a/crypto/src/crypto/tls/TlsKeyExchange.cs b/crypto/src/crypto/tls/TlsKeyExchange.cs
index 5102edbec..6731f6f63 100644 --- a/crypto/src/crypto/tls/TlsKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -3,36 +3,52 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// A generic interface for key exchange implementations in TLS 1.0. - /// </summary> - public interface TlsKeyExchange - { - /// <exception cref="IOException"/> - void SkipServerCertificate(); - - /// <exception cref="IOException"/> - void ProcessServerCertificate(Certificate serverCertificate); - - /// <exception cref="IOException"/> - void SkipServerKeyExchange(); - - /// <exception cref="IOException"/> - void ProcessServerKeyExchange(Stream input); - - /// <exception cref="IOException"/> - void ValidateCertificateRequest(CertificateRequest certificateRequest); - - /// <exception cref="IOException"/> - void SkipClientCredentials(); - - /// <exception cref="IOException"/> - void ProcessClientCredentials(TlsCredentials clientCredentials); - - /// <exception cref="IOException"/> - void GenerateClientKeyExchange(Stream output); - - /// <exception cref="IOException"/> - byte[] GeneratePremasterSecret(); - } + /// <summary> + /// A generic interface for key exchange implementations in (D)TLS. + /// </summary> + public interface TlsKeyExchange + { + void Init(TlsContext context); + + /// <exception cref="IOException"/> + void SkipServerCredentials(); + + /// <exception cref="IOException"/> + void ProcessServerCredentials(TlsCredentials serverCredentials); + + /// <exception cref="IOException"/> + void ProcessServerCertificate(Certificate serverCertificate); + + bool RequiresServerKeyExchange { get; } + + /// <exception cref="IOException"/> + byte[] GenerateServerKeyExchange(); + + /// <exception cref="IOException"/> + void SkipServerKeyExchange(); + + /// <exception cref="IOException"/> + void ProcessServerKeyExchange(Stream input); + + /// <exception cref="IOException"/> + void ValidateCertificateRequest(CertificateRequest certificateRequest); + + /// <exception cref="IOException"/> + void SkipClientCredentials(); + + /// <exception cref="IOException"/> + void ProcessClientCredentials(TlsCredentials clientCredentials); + + /// <exception cref="IOException"/> + void ProcessClientCertificate(Certificate clientCertificate); + + /// <exception cref="IOException"/> + void GenerateClientKeyExchange(Stream output); + + /// <exception cref="IOException"/> + void ProcessClientKeyExchange(Stream input); + + /// <exception cref="IOException"/> + byte[] GeneratePremasterSecret(); + } } diff --git a/crypto/src/crypto/tls/TlsMac.cs b/crypto/src/crypto/tls/TlsMac.cs
index e4313617e..a80319a17 100644 --- a/crypto/src/crypto/tls/TlsMac.cs +++ b/crypto/src/crypto/tls/TlsMac.cs
@@ -9,134 +9,165 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - /// <remarks> - /// A generic TLS MAC implementation, which can be used with any kind of - /// IDigest to act as an HMAC. - /// </remarks> + /// <summary> + /// A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest. + /// </summary> public class TlsMac { - protected long seqNo; - protected byte[] secret; - protected HMac mac; + protected readonly TlsContext context; + protected readonly byte[] secret; + protected readonly IMac mac; + protected readonly int digestBlockSize; + protected readonly int digestOverhead; + protected readonly int macLength; /** - * 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. - */ - public TlsMac( - IDigest digest, - byte[] key_block, - int offset, - int len) + * Generate a new instance of an TlsMac. + * + * @param context the TLS client context + * @param digest The digest to use. + * @param key A byte-array where the key for this MAC is located. + * @param keyOff The number of bytes to skip, before the key starts in the buffer. + * @param keyLen The length of the key. + */ + public TlsMac(TlsContext context, IDigest digest, byte[] key, int keyOff, int keyLen) { - this.seqNo = 0; + this.context = context; - KeyParameter param = new KeyParameter(key_block, offset, len); + KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen); - this.secret = Arrays.Clone(param.GetKey()); + this.secret = Arrays.Clone(keyParameter.GetKey()); - this.mac = new HMac(digest); - this.mac.Init(param); - } + // TODO This should check the actual algorithm, not rely on the engine type + if (digest is LongDigest) + { + this.digestBlockSize = 128; + this.digestOverhead = 16; + } + else + { + this.digestBlockSize = 64; + this.digestOverhead = 8; + } - /** - * @return the MAC write secret - */ - public virtual byte[] GetMacSecret() - { - return this.secret; - } + if (TlsUtilities.IsSsl(context)) + { + this.mac = new Ssl3Mac(digest); - /** - * @return the current write sequence number - */ - public virtual long SequenceNumber - { - get { return this.seqNo; } + // TODO This should check the actual algorithm, not assume based on the digest size + if (digest.GetDigestSize() == 20) + { + /* + * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not + * digest block-aligned. + */ + this.digestOverhead = 4; + } + } + else + { + this.mac = new HMac(digest); + + // NOTE: The input pad for HMAC is always a full digest block + } + + this.mac.Init(keyParameter); + + this.macLength = mac.GetMacSize(); + if (context.SecurityParameters.truncatedHMac) + { + this.macLength = System.Math.Min(this.macLength, 10); + } } /** - * Increment the current write sequence number + * @return the MAC write secret */ - public virtual void IncSequenceNumber() + public virtual byte[] MacSecret { - this.seqNo++; + get { return this.secret; } } /** - * @return The Keysize of the mac. - */ + * @return The output length of this MAC. + */ public virtual int Size { - get { return mac.GetMacSize(); } + get { return macLength; } } /** - * Calculate the mac for some given data. - * <p/> - * 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. - */ - public virtual byte[] CalculateMac(byte type, byte[] message, int offset, int len) + * Calculate the MAC for some given data. + * + * @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 length The length of the message. + * @return A new byte-buffer containing the MAC value. + */ + public virtual byte[] CalculateMac(long seqNo, byte type, byte[] message, int offset, int length) { - //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion; - bool isTls = true; + ProtocolVersion serverVersion = context.ServerVersion; + bool isSsl = serverVersion.IsSsl; - byte[] macHeader = new byte[isTls ? 13 : 11]; - TlsUtilities.WriteUint64(seqNo++, macHeader, 0); + byte[] macHeader = new byte[isSsl ? 11 : 13]; + TlsUtilities.WriteUint64(seqNo, macHeader, 0); TlsUtilities.WriteUint8(type, macHeader, 8); - if (isTls) + if (!isSsl) { - TlsUtilities.WriteVersion(macHeader, 9); + TlsUtilities.WriteVersion(serverVersion, macHeader, 9); } - TlsUtilities.WriteUint16(len, macHeader, 11); + TlsUtilities.WriteUint16(length, macHeader, macHeader.Length - 2); mac.BlockUpdate(macHeader, 0, macHeader.Length); - mac.BlockUpdate(message, offset, len); - return MacUtilities.DoFinal(mac); + mac.BlockUpdate(message, offset, length); + + return Truncate(MacUtilities.DoFinal(mac)); } - public virtual byte[] CalculateMacConstantTime(byte type, byte[] message, int offset, int len, + public virtual byte[] CalculateMacConstantTime(long seqNo, byte type, byte[] message, int offset, int length, int fullLength, byte[] dummyData) { - // Actual MAC only calculated on 'len' bytes - byte[] result = CalculateMac(type, message, offset, len); + /* + * Actual MAC only calculated on 'length' bytes... + */ + byte[] result = CalculateMac(seqNo, type, message, offset, length); + + /* + * ...but ensure a constant number of complete digest blocks are processed (as many as would + * be needed for 'fullLength' bytes of input). + */ + int headerLength = TlsUtilities.IsSsl(context) ? 11 : 13; - //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion; - bool isTls = true; + // How many extra full blocks do we need to calculate? + int extra = GetDigestBlockCount(headerLength + fullLength) - GetDigestBlockCount(headerLength + length); - // ...but ensure a constant number of complete digest blocks are processed (per 'fullLength') - if (isTls) + while (--extra >= 0) { - // TODO Currently all TLS digests use a block size of 64, a suffix (length field) of 8, and padding (1+) - int db = 64, ds = 8; + mac.BlockUpdate(dummyData, 0, digestBlockSize); + } - int L1 = 13 + fullLength; - int L2 = 13 + len; + // One more byte in case the implementation is "lazy" about processing blocks + mac.Update(dummyData[0]); + mac.Reset(); - // How many extra full blocks do we need to calculate? - int extra = ((L1 + ds) / db) - ((L2 + ds) / db); + return result; + } - while (--extra >= 0) - { - mac.BlockUpdate(dummyData, 0, db); - } + protected virtual int GetDigestBlockCount(int inputLength) + { + // NOTE: This calculation assumes a minimum of 1 pad byte + return (inputLength + digestOverhead) / digestBlockSize; + } - // One more byte in case the implementation is "lazy" about processing blocks - mac.Update(dummyData[0]); - mac.Reset(); + protected virtual byte[] Truncate(byte[] bs) + { + if (bs.Length <= macLength) + { + return bs; } - return result; + return Arrays.CopyOf(bs, macLength); } } } diff --git a/crypto/src/crypto/tls/TlsNullCipher.cs b/crypto/src/crypto/tls/TlsNullCipher.cs
index 3e2bfa847..f30ace24f 100644 --- a/crypto/src/crypto/tls/TlsNullCipher.cs +++ b/crypto/src/crypto/tls/TlsNullCipher.cs
@@ -1,28 +1,118 @@ using System; +using System.IO; + +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { /// <summary> - /// A NULL cipher suite, for use during handshake. + /// A NULL CipherSuite, with optional MAC. /// </summary> public class TlsNullCipher - : TlsCipher + : TlsCipher { - public virtual byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len) + protected readonly TlsContext context; + + protected readonly TlsMac writeMac; + protected readonly TlsMac readMac; + + public TlsNullCipher(TlsContext context) { - return CopyData(plaintext, offset, len); + this.context = context; + this.writeMac = null; + this.readMac = null; } - public virtual byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len) + /// <exception cref="IOException"></exception> + public TlsNullCipher(TlsContext context, IDigest clientWriteDigest, IDigest serverWriteDigest) { - return CopyData(ciphertext, offset, len); + if ((clientWriteDigest == null) != (serverWriteDigest == null)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.context = context; + + TlsMac clientWriteMac = null, serverWriteMac = null; + + if (clientWriteDigest != null) + { + int key_block_size = clientWriteDigest.GetDigestSize() + + serverWriteDigest.GetDigestSize(); + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); + + int offset = 0; + + clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.GetDigestSize()); + offset += clientWriteDigest.GetDigestSize(); + + serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.GetDigestSize()); + offset += serverWriteDigest.GetDigestSize(); + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + if (context.IsServer) + { + writeMac = serverWriteMac; + readMac = clientWriteMac; + } + else + { + writeMac = clientWriteMac; + readMac = serverWriteMac; + } } - protected virtual byte[] CopyData(byte[] text, int offset, int len) + public virtual int GetPlaintextLimit(int ciphertextLimit) { - byte[] result = new byte[len]; - Array.Copy(text, offset, result, 0, len); + int result = ciphertextLimit; + if (writeMac != null) + { + result -= writeMac.Size; + } return result; } + + /// <exception cref="IOException"></exception> + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + if (writeMac == null) + { + return Arrays.CopyOfRange(plaintext, offset, offset + len); + } + + byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len); + byte[] ciphertext = new byte[len + mac.Length]; + Array.Copy(plaintext, offset, ciphertext, 0, len); + Array.Copy(mac, 0, ciphertext, len, mac.Length); + return ciphertext; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + if (readMac == null) + { + return Arrays.CopyOfRange(ciphertext, offset, offset + len); + } + + int macSize = readMac.Size; + if (len < macSize) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int macInputLen = len - macSize; + + byte[] receivedMac = Arrays.CopyOfRange(ciphertext, offset + macInputLen, offset + len); + byte[] computedMac = readMac.CalculateMac(seqNo, type, ciphertext, offset, macInputLen); + + if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac)) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + + return Arrays.CopyOfRange(ciphertext, offset, offset + macInputLen); + } } } diff --git a/crypto/src/crypto/tls/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs
index 5b5c94a44..1ae41a41a 100644 --- a/crypto/src/crypto/tls/TlsPeer.cs +++ b/crypto/src/crypto/tls/TlsPeer.cs
@@ -1,4 +1,5 @@ using System; +using System.IO; namespace Org.BouncyCastle.Crypto.Tls { @@ -15,5 +16,47 @@ namespace Org.BouncyCastle.Crypto.Tls /// random value. /// </returns> bool ShouldUseGmtUnixTime(); + + /// <summary> + /// Report whether the server supports secure renegotiation + /// </summary> + /// <remarks> + /// The protocol handler automatically processes the relevant extensions + /// </remarks> + /// <param name="secureRenegotiation"> + /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation + /// </param> + /// <exception cref="IOException"></exception> + void NotifySecureRenegotiation(bool secureRenegotiation); + + /// <summary> + /// Return an implementation of <see cref="TlsCompression"/> to handle record compression. + /// </summary> + /// <returns>A <see cref="TlsCompression"/></returns> + /// <exception cref="IOException"/> + TlsCompression GetCompression(); + + /// <summary> + /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption. + /// </summary> + /// <returns>A <see cref="TlsCipher"/></returns> + /// <exception cref="IOException"/> + TlsCipher GetCipher(); + + /// <summary>This method will be called when an alert is raised by the protocol.</summary> + /// <param name="alertLevel"><see cref="AlertLevel"/></param> + /// <param name="alertDescription"><see cref="AlertDescription"/></param> + /// <param name="message">A human-readable message explaining what caused this alert. May be null.</param> + /// <param name="cause">The <c>Exception</c> that caused this alert to be raised. May be null.</param> + void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause); + + /// <summary>This method will be called when an alert is received from the remote peer.</summary> + /// <param name="alertLevel"><see cref="AlertLevel"/></param> + /// <param name="alertDescription"><see cref="AlertDescription"/></param> + void NotifyAlertReceived(byte alertLevel, byte alertDescription); + + /// <summary>Notifies the peer that the handshake has been successfully completed.</summary> + /// <exception cref="IOException"></exception> + void NotifyHandshakeComplete(); } } diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs new file mode 100644
index 000000000..7acc34d3c --- /dev/null +++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -0,0 +1,1318 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsProtocol + { + private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack"; + + /* + * Our Connection states + */ + protected const short CS_START = 0; + protected const short CS_CLIENT_HELLO = 1; + protected const short CS_SERVER_HELLO = 2; + protected const short CS_SERVER_SUPPLEMENTAL_DATA = 3; + protected const short CS_SERVER_CERTIFICATE = 4; + protected const short CS_CERTIFICATE_STATUS = 5; + protected const short CS_SERVER_KEY_EXCHANGE = 6; + protected const short CS_CERTIFICATE_REQUEST = 7; + protected const short CS_SERVER_HELLO_DONE = 8; + protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 9; + protected const short CS_CLIENT_CERTIFICATE = 10; + protected const short CS_CLIENT_KEY_EXCHANGE = 11; + protected const short CS_CERTIFICATE_VERIFY = 12; + protected const short CS_CLIENT_FINISHED = 13; + protected const short CS_SERVER_SESSION_TICKET = 14; + protected const short CS_SERVER_FINISHED = 15; + protected const short CS_END = 16; + + /* + * Queues for data from some protocols. + */ + private ByteQueue mApplicationDataQueue = new ByteQueue(); + private ByteQueue mAlertQueue = new ByteQueue(2); + private ByteQueue mHandshakeQueue = new ByteQueue(); + // private ByteQueue mHeartbeatQueue = new ByteQueue(); + + /* + * The Record Stream we use + */ + internal RecordStream mRecordStream; + protected SecureRandom mSecureRandom; + + private TlsStream mTlsStream = null; + + private volatile bool mClosed = false; + private volatile bool mFailedWithError = false; + private volatile bool mAppDataReady = false; + private volatile bool mSplitApplicationDataRecords = true; + private byte[] mExpectedVerifyData = null; + + protected TlsSession mTlsSession = null; + protected SessionParameters mSessionParameters = null; + protected SecurityParameters mSecurityParameters = null; + protected Certificate mPeerCertificate = null; + + protected int[] mOfferedCipherSuites = null; + protected byte[] mOfferedCompressionMethods = null; + protected IDictionary mClientExtensions = null; + protected IDictionary mServerExtensions = null; + + protected short mConnectionState = CS_START; + protected bool mResumedSession = false; + protected bool mReceivedChangeCipherSpec = false; + protected bool mSecureRenegotiation = false; + protected bool mAllowCertificateStatus = false; + protected bool mExpectSessionTicket = false; + + protected bool mBlocking = true; + protected ByteQueueStream mInputBuffers = null; + protected ByteQueueStream mOutputBuffer = null; + + public TlsProtocol(Stream stream, SecureRandom secureRandom) + : this(stream, stream, secureRandom) + { + } + + public TlsProtocol(Stream input, Stream output, SecureRandom secureRandom) + { + this.mRecordStream = new RecordStream(this, input, output); + this.mSecureRandom = secureRandom; + } + + public TlsProtocol(SecureRandom secureRandom) + { + this.mBlocking = false; + this.mInputBuffers = new ByteQueueStream(); + this.mOutputBuffer = new ByteQueueStream(); + this.mRecordStream = new RecordStream(this, mInputBuffers, mOutputBuffer); + this.mSecureRandom = secureRandom; + } + + protected abstract TlsContext Context { get; } + + internal abstract AbstractTlsContext ContextAdmin { get; } + + protected abstract TlsPeer Peer { get; } + + protected virtual void HandleChangeCipherSpecMessage() + { + } + + protected abstract void HandleHandshakeMessage(byte type, byte[] buf); + + protected virtual void HandleWarningMessage(byte description) + { + } + + protected virtual void ApplyMaxFragmentLengthExtension() + { + if (mSecurityParameters.maxFragmentLength >= 0) + { + if (!MaxFragmentLength.IsValid((byte)mSecurityParameters.maxFragmentLength)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + int plainTextLimit = 1 << (8 + mSecurityParameters.maxFragmentLength); + mRecordStream.SetPlaintextLimit(plainTextLimit); + } + } + + protected virtual void CheckReceivedChangeCipherSpec(bool expected) + { + if (expected != mReceivedChangeCipherSpec) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + protected virtual void CleanupHandshake() + { + if (this.mExpectedVerifyData != null) + { + Arrays.Fill(this.mExpectedVerifyData, (byte)0); + this.mExpectedVerifyData = null; + } + + this.mSecurityParameters.Clear(); + this.mPeerCertificate = null; + + this.mOfferedCipherSuites = null; + this.mOfferedCompressionMethods = null; + this.mClientExtensions = null; + this.mServerExtensions = null; + + this.mResumedSession = false; + this.mReceivedChangeCipherSpec = false; + this.mSecureRenegotiation = false; + this.mAllowCertificateStatus = false; + this.mExpectSessionTicket = false; + } + + protected virtual void BlockForHandshake() + { + if (mBlocking) + { + while (this.mConnectionState != CS_END) + { + if (this.mClosed) + { + // TODO What kind of exception/alert? + } + + SafeReadRecord(); + } + } + } + + protected virtual void CompleteHandshake() + { + try + { + this.mRecordStream.FinaliseHandshake(); + + this.mSplitApplicationDataRecords = !TlsUtilities.IsTlsV11(Context); + + /* + * If this was an initial handshake, we are now ready to send and receive application data. + */ + if (!mAppDataReady) + { + this.mAppDataReady = true; + + if (mBlocking) + { + this.mTlsStream = new TlsStream(this); + } + } + + if (this.mTlsSession != null) + { + if (this.mSessionParameters == null) + { + this.mSessionParameters = new SessionParameters.Builder() + .SetCipherSuite(this.mSecurityParameters.CipherSuite) + .SetCompressionAlgorithm(this.mSecurityParameters.CompressionAlgorithm) + .SetMasterSecret(this.mSecurityParameters.MasterSecret) + .SetPeerCertificate(this.mPeerCertificate) + .SetPskIdentity(this.mSecurityParameters.PskIdentity) + .SetSrpIdentity(this.mSecurityParameters.SrpIdentity) + // TODO Consider filtering extensions that aren't relevant to resumed sessions + .SetServerExtensions(this.mServerExtensions) + .Build(); + + this.mTlsSession = new TlsSessionImpl(this.mTlsSession.SessionID, this.mSessionParameters); + } + + ContextAdmin.SetResumableSession(this.mTlsSession); + } + + Peer.NotifyHandshakeComplete(); + } + finally + { + CleanupHandshake(); + } + } + + protected internal void ProcessRecord(byte protocol, byte[] buf, int offset, int len) + { + /* + * Have a look at the protocol type, and add it to the correct queue. + */ + switch (protocol) + { + case ContentType.alert: + { + mAlertQueue.AddData(buf, offset, len); + ProcessAlert(); + break; + } + case ContentType.application_data: + { + if (!mAppDataReady) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + mApplicationDataQueue.AddData(buf, offset, len); + ProcessApplicationData(); + break; + } + case ContentType.change_cipher_spec: + { + ProcessChangeCipherSpec(buf, offset, len); + break; + } + case ContentType.handshake: + { + mHandshakeQueue.AddData(buf, offset, len); + ProcessHandshake(); + break; + } + case ContentType.heartbeat: + { + if (!mAppDataReady) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + // TODO[RFC 6520] + // mHeartbeatQueue.AddData(buf, offset, len); + // ProcessHeartbeat(); + 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 (mHandshakeQueue.Available >= 4) + { + byte[] beginning = new byte[4]; + mHandshakeQueue.Read(beginning, 0, 4, 0); + byte type = TlsUtilities.ReadUint8(beginning, 0); + int len = TlsUtilities.ReadUint24(beginning, 1); + + /* + * Check if we have enough bytes in the buffer to read the full message. + */ + if (mHandshakeQueue.Available >= (len + 4)) + { + /* + * Read the message. + */ + byte[] buf = mHandshakeQueue.RemoveData(len, 4); + + CheckReceivedChangeCipherSpec(mConnectionState == CS_END || type == HandshakeType.finished); + + /* + * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages + * starting at client hello up to, but not including, this finished message. + * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes. + */ + switch (type) + { + case HandshakeType.hello_request: + break; + case HandshakeType.finished: + default: + { + TlsContext ctx = Context; + if (type == HandshakeType.finished + && this.mExpectedVerifyData == null + && ctx.SecurityParameters.MasterSecret != null) + { + this.mExpectedVerifyData = CreateVerifyData(!ctx.IsServer); + } + + mRecordStream.UpdateHandshakeData(beginning, 0, 4); + mRecordStream.UpdateHandshakeData(buf, 0, len); + break; + } + } + + /* + * Now, parse the message. + */ + HandleHandshakeMessage(type, buf); + read = true; + } + } + } + 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 (mAlertQueue.Available >= 2) + { + /* + * An alert is always 2 bytes. Read the alert. + */ + byte[] tmp = mAlertQueue.RemoveData(2, 0); + byte level = tmp[0]; + byte description = tmp[1]; + + Peer.NotifyAlertReceived(level, description); + + if (level == AlertLevel.fatal) + { + /* + * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated + * without proper close_notify messages with level equal to warning. + */ + InvalidateSession(); + + this.mFailedWithError = true; + this.mClosed = true; + + mRecordStream.SafeClose(); + + throw new IOException(TLS_ERROR_MESSAGE); + } + else + { + + /* + * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own + * and close down the connection immediately, discarding any pending writes. + */ + // TODO Can close_notify be a fatal alert? + if (description == AlertDescription.close_notify) + { + HandleClose(false); + } + + /* + * If it is just a warning, we continue. + */ + HandleWarningMessage(description); + } + } + } + + /** + * 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(byte[] buf, int off, int len) + { + for (int i = 0; i < len; ++i) + { + byte message = TlsUtilities.ReadUint8(buf, off + i); + + if (message != ChangeCipherSpec.change_cipher_spec) + throw new TlsFatalAlert(AlertDescription.decode_error); + + if (this.mReceivedChangeCipherSpec + || mAlertQueue.Available > 0 + || mHandshakeQueue.Available > 0) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + mRecordStream.ReceivedReadCipherSpec(); + + this.mReceivedChangeCipherSpec = true; + + HandleChangeCipherSpecMessage(); + } + } + + protected internal virtual int ApplicationDataAvailable() + { + return mApplicationDataQueue.Available; + } + + /** + * Read data from the network. The method will return immediately, if there is still some data + * left in the buffer, or block until some application data has been read from the network. + * + * @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. + */ + protected internal virtual int ReadApplicationData(byte[] buf, int offset, int len) + { + if (len < 1) + return 0; + + while (mApplicationDataQueue.Available == 0) + { + /* + * We need to read some data. + */ + if (this.mClosed) + { + if (this.mFailedWithError) + { + /* + * Something went terribly wrong, we should throw an IOException + */ + throw new IOException(TLS_ERROR_MESSAGE); + } + + /* + * Connection has been closed, there is no more data to read. + */ + return 0; + } + + SafeReadRecord(); + } + + len = System.Math.Min(len, mApplicationDataQueue.Available); + mApplicationDataQueue.RemoveData(buf, offset, len, 0); + return len; + } + + protected virtual void SafeReadRecord() + { + try + { + if (!mRecordStream.ReadRecord()) + { + // TODO It would be nicer to allow graceful connection close if between records + // this.FailWithError(AlertLevel.warning, AlertDescription.close_notify); + throw new EndOfStreamException(); + } + } + catch (TlsFatalAlert e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e); + } + throw e; + } + catch (Exception e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); + } + throw e; + } + } + + protected virtual void SafeWriteRecord(byte type, byte[] buf, int offset, int len) + { + try + { + mRecordStream.WriteRecord(type, buf, offset, len); + } + catch (TlsFatalAlert e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to write record", e); + } + throw e; + } + catch (Exception e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e); + } + throw e; + } + } + + /** + * Send some application data to the remote system. + * <p/> + * 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. + */ + protected internal virtual void WriteData(byte[] buf, int offset, int len) + { + if (this.mClosed) + { + if (this.mFailedWithError) + throw new IOException(TLS_ERROR_MESSAGE); + + throw new IOException("Sorry, connection has been closed, you cannot write more data"); + } + + while (len > 0) + { + /* + * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are + * potentially useful as a traffic analysis countermeasure. + * + * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting. + */ + + if (this.mSplitApplicationDataRecords) + { + /* + * Protect against known IV attack! + * + * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. + */ + SafeWriteRecord(ContentType.application_data, buf, offset, 1); + ++offset; + --len; + } + + if (len > 0) + { + // Fragment data according to the current fragment limit. + int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit()); + SafeWriteRecord(ContentType.application_data, buf, offset, toWrite); + offset += toWrite; + len -= toWrite; + } + } + } + + protected virtual void WriteHandshakeMessage(byte[] buf, int off, int len) + { + while (len > 0) + { + // Fragment data according to the current fragment limit. + int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit()); + SafeWriteRecord(ContentType.handshake, buf, off, toWrite); + off += toWrite; + len -= toWrite; + } + } + + /// <summary>The secure bidirectional stream for this connection</summary> + /// <remarks>Only allowed in blocking mode.</remarks> + public virtual Stream Stream + { + get + { + if (!mBlocking) + throw new InvalidOperationException("Cannot use Stream in non-blocking mode! Use OfferInput()/OfferOutput() instead."); + return this.mTlsStream; + } + } + + /** + * Offer input from an arbitrary source. Only allowed in non-blocking mode.<br> + * <br> + * After this method returns, the input buffer is "owned" by this object. Other code + * must not attempt to do anything with it.<br> + * <br> + * This method will decrypt and process all records that are fully available. + * If only part of a record is available, the buffer will be retained until the + * remainder of the record is offered.<br> + * <br> + * If any records containing application data were processed, the decrypted data + * can be obtained using {@link #readInput(byte[], int, int)}. If any records + * containing protocol data were processed, a response may have been generated. + * You should always check to see if there is any available output after calling + * this method by calling {@link #getAvailableOutputBytes()}. + * @param input The input buffer to offer + * @throws IOException If an error occurs while decrypting or processing a record + */ + public virtual void OfferInput(byte[] input) + { + if (mBlocking) + throw new InvalidOperationException("Cannot use OfferInput() in blocking mode! Use Stream instead."); + if (mClosed) + throw new IOException("Connection is closed, cannot accept any more input"); + + mInputBuffers.Write(input); + + // loop while there are enough bytes to read the length of the next record + while (mInputBuffers.Available >= RecordStream.TLS_HEADER_SIZE) + { + byte[] header = new byte[RecordStream.TLS_HEADER_SIZE]; + mInputBuffers.Peek(header); + + int totalLength = TlsUtilities.ReadUint16(header, RecordStream.TLS_HEADER_LENGTH_OFFSET) + RecordStream.TLS_HEADER_SIZE; + if (mInputBuffers.Available < totalLength) + { + // not enough bytes to read a whole record + break; + } + + SafeReadRecord(); + } + } + + /** + * Gets the amount of received application data. A call to {@link #readInput(byte[], int, int)} + * is guaranteed to be able to return at least this much data.<br> + * <br> + * Only allowed in non-blocking mode. + * @return The number of bytes of available application data + */ + public virtual int GetAvailableInputBytes() + { + if (mBlocking) + throw new InvalidOperationException("Cannot use GetAvailableInputBytes() in blocking mode! Use ApplicationDataAvailable() instead."); + + return ApplicationDataAvailable(); + } + + /** + * Retrieves received application data. Use {@link #getAvailableInputBytes()} to check + * how much application data is currently available. This method functions similarly to + * {@link InputStream#read(byte[], int, int)}, except that it never blocks. If no data + * is available, nothing will be copied and zero will be returned.<br> + * <br> + * Only allowed in non-blocking mode. + * @param buffer The buffer to hold the application data + * @param offset The start offset in the buffer at which the data is written + * @param length The maximum number of bytes to read + * @return The total number of bytes copied to the buffer. May be less than the + * length specified if the length was greater than the amount of available data. + */ + public virtual int ReadInput(byte[] buffer, int offset, int length) + { + if (mBlocking) + throw new InvalidOperationException("Cannot use ReadInput() in blocking mode! Use Stream instead."); + + return ReadApplicationData(buffer, offset, System.Math.Min(length, ApplicationDataAvailable())); + } + + /** + * Offer output from an arbitrary source. Only allowed in non-blocking mode.<br> + * <br> + * After this method returns, the specified section of the buffer will have been + * processed. Use {@link #readOutput(byte[], int, int)} to get the bytes to + * transmit to the other peer.<br> + * <br> + * This method must not be called until after the handshake is complete! Attempting + * to call it before the handshake is complete will result in an exception. + * @param buffer The buffer containing application data to encrypt + * @param offset The offset at which to begin reading data + * @param length The number of bytes of data to read + * @throws IOException If an error occurs encrypting the data, or the handshake is not complete + */ + public virtual void OfferOutput(byte[] buffer, int offset, int length) + { + if (mBlocking) + throw new InvalidOperationException("Cannot use OfferOutput() in blocking mode! Use Stream instead."); + if (!mAppDataReady) + throw new IOException("Application data cannot be sent until the handshake is complete!"); + + WriteData(buffer, offset, length); + } + + /** + * Gets the amount of encrypted data available to be sent. A call to + * {@link #readOutput(byte[], int, int)} is guaranteed to be able to return at + * least this much data.<br> + * <br> + * Only allowed in non-blocking mode. + * @return The number of bytes of available encrypted data + */ + public virtual int GetAvailableOutputBytes() + { + if (mBlocking) + throw new InvalidOperationException("Cannot use GetAvailableOutputBytes() in blocking mode! Use Stream instead."); + + return mOutputBuffer.Available; + } + + /** + * Retrieves encrypted data to be sent. Use {@link #getAvailableOutputBytes()} to check + * how much encrypted data is currently available. This method functions similarly to + * {@link InputStream#read(byte[], int, int)}, except that it never blocks. If no data + * is available, nothing will be copied and zero will be returned.<br> + * <br> + * Only allowed in non-blocking mode. + * @param buffer The buffer to hold the encrypted data + * @param offset The start offset in the buffer at which the data is written + * @param length The maximum number of bytes to read + * @return The total number of bytes copied to the buffer. May be less than the + * length specified if the length was greater than the amount of available data. + */ + public virtual int ReadOutput(byte[] buffer, int offset, int length) + { + if (mBlocking) + throw new InvalidOperationException("Cannot use ReadOutput() in blocking mode! Use Stream instead."); + + return mOutputBuffer.Read(buffer, offset, length); + } + + /** + * Terminate this connection with an alert. Can be used for normal closure too. + * + * @param alertLevel + * See {@link AlertLevel} for values. + * @param alertDescription + * See {@link AlertDescription} for values. + * @throws IOException + * If alert was fatal. + */ + protected virtual void FailWithError(byte alertLevel, byte alertDescription, string message, Exception cause) + { + /* + * Check if the connection is still open. + */ + if (!mClosed) + { + /* + * Prepare the message + */ + this.mClosed = true; + + if (alertLevel == AlertLevel.fatal) + { + /* + * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated + * without proper close_notify messages with level equal to warning. + */ + // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete. + InvalidateSession(); + + this.mFailedWithError = true; + } + RaiseAlert(alertLevel, alertDescription, message, cause); + mRecordStream.SafeClose(); + if (alertLevel != AlertLevel.fatal) + { + return; + } + } + + throw new IOException(TLS_ERROR_MESSAGE); + } + + protected virtual void InvalidateSession() + { + if (this.mSessionParameters != null) + { + this.mSessionParameters.Clear(); + this.mSessionParameters = null; + } + + if (this.mTlsSession != null) + { + this.mTlsSession.Invalidate(); + this.mTlsSession = null; + } + } + + protected virtual void ProcessFinishedMessage(MemoryStream buf) + { + if (mExpectedVerifyData == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + byte[] verify_data = TlsUtilities.ReadFully(mExpectedVerifyData.Length, buf); + + AssertEmpty(buf); + + /* + * Compare both checksums. + */ + if (!Arrays.ConstantTimeAreEqual(mExpectedVerifyData, verify_data)) + { + /* + * Wrong checksum in the finished message. + */ + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + + protected virtual void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause) + { + Peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause); + + byte[] error = new byte[]{ alertLevel, alertDescription }; + + SafeWriteRecord(ContentType.alert, error, 0, 2); + } + + protected virtual void RaiseWarning(byte alertDescription, string message) + { + RaiseAlert(AlertLevel.warning, alertDescription, message, null); + } + + protected virtual void SendCertificateMessage(Certificate certificate) + { + if (certificate == null) + { + certificate = Certificate.EmptyChain; + } + + if (certificate.IsEmpty) + { + TlsContext context = Context; + if (!context.IsServer) + { + ProtocolVersion serverVersion = Context.ServerVersion; + if (serverVersion.IsSsl) + { + string errorMessage = serverVersion.ToString() + " client didn't provide credentials"; + RaiseWarning(AlertDescription.no_certificate, errorMessage); + return; + } + } + } + + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate); + + certificate.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendChangeCipherSpecMessage() + { + byte[] message = new byte[]{ 1 }; + SafeWriteRecord(ContentType.change_cipher_spec, message, 0, message.Length); + mRecordStream.SentWriteCipherSpec(); + } + + protected virtual void SendFinishedMessage() + { + byte[] verify_data = CreateVerifyData(Context.IsServer); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.Length); + + message.Write(verify_data, 0, verify_data.Length); + + message.WriteToRecordStream(this); + } + + protected virtual void SendSupplementalDataMessage(IList supplementalData) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data); + + WriteSupplementalData(message, supplementalData); + + message.WriteToRecordStream(this); + } + + protected virtual byte[] CreateVerifyData(bool isServer) + { + TlsContext context = Context; + string asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished; + byte[] sslSender = isServer ? TlsUtilities.SSL_SERVER : TlsUtilities.SSL_CLIENT; + byte[] hash = GetCurrentPrfHash(context, mRecordStream.HandshakeHash, sslSender); + return TlsUtilities.CalculateVerifyData(context, asciiLabel, hash); + } + + /** + * Closes this connection. + * + * @throws IOException If something goes wrong during closing. + */ + public virtual void Close() + { + HandleClose(true); + } + + protected virtual void HandleClose(bool user_canceled) + { + if (!mClosed) + { + if (user_canceled && !mAppDataReady) + { + RaiseWarning(AlertDescription.user_canceled, "User canceled handshake"); + } + this.FailWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null); + } + } + + protected internal virtual void Flush() + { + mRecordStream.Flush(); + } + + public virtual bool IsClosed + { + get { return mClosed; } + } + + protected virtual short ProcessMaxFragmentLengthExtension(IDictionary clientExtensions, IDictionary serverExtensions, + byte alertDescription) + { + short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions); + if (maxFragmentLength >= 0) + { + if (!MaxFragmentLength.IsValid((byte)maxFragmentLength) + || (!this.mResumedSession && maxFragmentLength != TlsExtensionsUtilities + .GetMaxFragmentLengthExtension(clientExtensions))) + { + throw new TlsFatalAlert(alertDescription); + } + } + return maxFragmentLength; + } + + protected virtual void RefuseRenegotiation() + { + /* + * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal + * handshake_failure alert. + */ + if (TlsUtilities.IsSsl(Context)) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + RaiseWarning(AlertDescription.no_renegotiation, "Renegotiation not supported"); + } + + /** + * Make sure the InputStream 'buf' now empty. Fail otherwise. + * + * @param buf The InputStream to check. + * @throws IOException If 'buf' is not empty. + */ + protected internal static void AssertEmpty(MemoryStream buf) + { + if (buf.Position < buf.Length) + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + protected internal static byte[] CreateRandomBlock(bool useGmtUnixTime, IRandomGenerator randomGenerator) + { + byte[] result = new byte[32]; + randomGenerator.NextBytes(result); + + if (useGmtUnixTime) + { + TlsUtilities.WriteGmtUnixTime(result, 0); + } + + return result; + } + + protected internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection) + { + return TlsUtilities.EncodeOpaque8(renegotiated_connection); + } + + protected internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange) + { + byte[] pre_master_secret = keyExchange.GeneratePremasterSecret(); + + try + { + context.SecurityParameters.masterSecret = TlsUtilities.CalculateMasterSecret(context, pre_master_secret); + } + finally + { + // TODO Is there a way to ensure the data is really overwritten? + /* + * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the + * master_secret has been computed. + */ + if (pre_master_secret != null) + { + Arrays.Fill(pre_master_secret, (byte)0); + } + } + } + + /** + * 'sender' only relevant to SSLv3 + */ + protected internal static byte[] GetCurrentPrfHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender) + { + IDigest d = handshakeHash.ForkPrfHash(); + + if (sslSender != null && TlsUtilities.IsSsl(context)) + { + d.BlockUpdate(sslSender, 0, sslSender.Length); + } + + return DigestUtilities.DoFinal(d); + } + + protected internal static IDictionary ReadExtensions(MemoryStream input) + { + if (input.Position >= input.Length) + return null; + + byte[] extBytes = TlsUtilities.ReadOpaque16(input); + + AssertEmpty(input); + + MemoryStream buf = new MemoryStream(extBytes, false); + + // Integer -> byte[] + IDictionary extensions = Platform.CreateHashtable(); + + while (buf.Position < buf.Length) + { + int extension_type = TlsUtilities.ReadUint16(buf); + byte[] extension_data = TlsUtilities.ReadOpaque16(buf); + + /* + * RFC 3546 2.3 There MUST NOT be more than one extension of the same type. + */ + if (extensions.Contains(extension_type)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + extensions.Add(extension_type, extension_data); + } + + return extensions; + } + + protected internal static IList ReadSupplementalDataMessage(MemoryStream input) + { + byte[] supp_data = TlsUtilities.ReadOpaque24(input); + + AssertEmpty(input); + + MemoryStream buf = new MemoryStream(supp_data, false); + + IList supplementalData = Platform.CreateArrayList(); + + while (buf.Position < buf.Length) + { + int supp_data_type = TlsUtilities.ReadUint16(buf); + byte[] data = TlsUtilities.ReadOpaque16(buf); + + supplementalData.Add(new SupplementalDataEntry(supp_data_type, data)); + } + + return supplementalData; + } + + protected internal static void WriteExtensions(Stream output, IDictionary extensions) + { + MemoryStream buf = new MemoryStream(); + + foreach (int extension_type in extensions.Keys) + { + byte[] extension_data = (byte[])extensions[extension_type]; + + TlsUtilities.CheckUint16(extension_type); + TlsUtilities.WriteUint16(extension_type, buf); + TlsUtilities.WriteOpaque16(extension_data, buf); + } + + byte[] extBytes = buf.ToArray(); + + TlsUtilities.WriteOpaque16(extBytes, output); + } + + protected internal static void WriteSupplementalData(Stream output, IList supplementalData) + { + MemoryStream buf = new MemoryStream(); + + foreach (SupplementalDataEntry entry in supplementalData) + { + int supp_data_type = entry.DataType; + TlsUtilities.CheckUint16(supp_data_type); + TlsUtilities.WriteUint16(supp_data_type, buf); + TlsUtilities.WriteOpaque16(entry.Data, buf); + } + + byte[] supp_data = buf.ToArray(); + + TlsUtilities.WriteOpaque24(supp_data, output); + } + + protected internal static int GetPrfAlgorithm(TlsContext context, int ciphersuite) + { + bool isTLSv12 = TlsUtilities.IsTlsV12(context); + + switch (ciphersuite) + { + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha256; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha384; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha384; + } + return PrfAlgorithm.tls_prf_legacy; + } + + default: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha256; + } + return PrfAlgorithm.tls_prf_legacy; + } + } + } + + internal class HandshakeMessage + : MemoryStream + { + internal HandshakeMessage(byte handshakeType) + : this(handshakeType, 60) + { + } + + internal HandshakeMessage(byte handshakeType, int length) + : base(length + 4) + { + TlsUtilities.WriteUint8(handshakeType, this); + // Reserve space for length + TlsUtilities.WriteUint24(0, this); + } + + internal void Write(byte[] data) + { + Write(data, 0, data.Length); + } + + internal void WriteToRecordStream(TlsProtocol protocol) + { + // Patch actual length back in + long length = Length - 4; + TlsUtilities.CheckUint24(length); + this.Position = 1; + TlsUtilities.WriteUint24((int)length, this); + protocol.WriteHandshakeMessage(GetBuffer(), 0, (int)Length); + this.Close(); + } + } + } +} diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 4707df3b5..6f223467f 100644 --- a/crypto/src/crypto/tls/TlsProtocolHandler.cs +++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -21,1224 +21,19 @@ using Org.BouncyCastle.Utilities.Date; namespace Org.BouncyCastle.Crypto.Tls { - /// <remarks>An implementation of all high level protocols in TLS 1.0.</remarks> + [Obsolete("Use 'TlsClientProtocol' instead")] public class TlsProtocolHandler + : TlsClientProtocol { - /* - * 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_CERTIFICATE_VERIFY_SEND = 8; - private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 9; - private const short CS_CLIENT_FINISHED_SEND = 10; - private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 11; - private const short CS_DONE = 12; - - 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 alertQueue = new ByteQueue(2); - private ByteQueue handshakeQueue = new ByteQueue(); - - /* - * The Record Stream we use - */ - private RecordStream rs; - private SecureRandom random; - - private TlsStream tlsStream = null; - - private bool closed = false; - private bool failedWithError = false; - private bool appDataReady = false; - private IDictionary clientExtensions; - - private SecurityParameters securityParameters = null; - - private TlsClientContextImpl tlsClientContext = null; - private TlsClient tlsClient = null; - private int[] offeredCipherSuites = null; - private byte[] offeredCompressionMethods = null; - private TlsKeyExchange keyExchange = null; - private TlsAuthentication authentication = null; - private CertificateRequest certificateRequest = null; - - private short connection_state = 0; - - private static SecureRandom CreateSecureRandom() - { - /* - * We use our threaded seed generator to generate a good random seed. If the user - * has a better random seed, he should use the constructor with a SecureRandom. - * - * Hopefully, 20 bytes in fast mode are good enough. - */ - byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true); - - return new SecureRandom(seed); - } - - public TlsProtocolHandler( - Stream s) - : this(s, s) - { - } - - public TlsProtocolHandler( - Stream s, - SecureRandom sr) - : this(s, s, sr) - { - } - - /// <remarks>Both streams can be the same object</remarks> - public TlsProtocolHandler( - Stream inStr, - Stream outStr) - : this(inStr, outStr, CreateSecureRandom()) + public TlsProtocolHandler(Stream stream, SecureRandom secureRandom) + : base(stream, stream, secureRandom) { } /// <remarks>Both streams can be the same object</remarks> - public TlsProtocolHandler( - Stream inStr, - Stream outStr, - SecureRandom sr) - { - this.rs = new RecordStream(this, inStr, outStr); - this.random = sr; - } - - internal void ProcessData( - byte contentType, - byte[] buf, - int offset, - int len) - { - /* - * Have a look at the protocol type, and add it to the correct queue. - */ - switch (contentType) - { - case ContentType.change_cipher_spec: - ProcessChangeCipherSpec(buf, offset, len); - break; - case ContentType.alert: - alertQueue.AddData(buf, offset, len); - ProcessAlert(); - break; - case ContentType.handshake: - handshakeQueue.AddData(buf, offset, len); - ProcessHandshake(); - break; - case ContentType.application_data: - if (!appDataReady) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } - 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); - byte handshakeType = 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 = handshakeQueue.RemoveData(len, 4); - - /* - * RFC 2246 7.4.9. The value handshake_messages includes all - * handshake messages starting at client hello up to, but not - * including, this finished message. [..] Note: [Also,] Hello Request - * messages are omitted from handshake hashes. - */ - switch (handshakeType) - { - case HandshakeType.hello_request: - case HandshakeType.finished: - break; - default: - rs.UpdateHandshakeData(beginning, 0, 4); - rs.UpdateHandshakeData(buf, 0, len); - break; - } - - /* - * Now, parse the message. - */ - ProcessHandshakeMessage(handshakeType, buf); - read = true; - } - } - } - while (read); - } - - private void ProcessHandshakeMessage(byte handshakeType, byte[] buf) - { - MemoryStream inStr = new MemoryStream(buf, false); - - /* - * Check the type. - */ - switch (handshakeType) - { - case HandshakeType.certificate: - { - switch (connection_state) - { - case CS_SERVER_HELLO_RECEIVED: - { - // Parse the Certificate message and send to cipher suite - - Certificate serverCertificate = Certificate.Parse(inStr); - - AssertEmpty(inStr); - - this.keyExchange.ProcessServerCertificate(serverCertificate); - - this.authentication = tlsClient.GetAuthentication(); - this.authentication.NotifyServerCertificate(serverCertificate); - - break; - } - default: - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; - } - - connection_state = CS_SERVER_CERTIFICATE_RECEIVED; - break; - } - case HandshakeType.finished: - switch (connection_state) - { - case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED: - /* - * Read the checksum from the finished message, it has always 12 bytes. - */ - byte[] serverVerifyData = new byte[12]; - TlsUtilities.ReadFully(serverVerifyData, inStr); - - AssertEmpty(inStr); - - /* - * Calculate our own checksum. - */ - byte[] expectedServerVerifyData = TlsUtilities.PRF( - securityParameters.masterSecret, "server finished", - rs.GetCurrentHash(), 12); - - /* - * Compare both checksums. - */ - if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData)) - { - /* - * Wrong checksum in the finished message. - */ - this.FailWithError(AlertLevel.fatal, AlertDescription.decrypt_error); - } - - connection_state = CS_DONE; - - /* - * We are now ready to receive application data. - */ - this.appDataReady = true; - break; - default: - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; - } - break; - case HandshakeType.server_hello: - switch (connection_state) - { - case CS_CLIENT_HELLO_SEND: - /* - * Read the server hello message - */ - TlsUtilities.CheckVersion(inStr); - - /* - * Read the server random - */ - securityParameters.serverRandom = new byte[32]; - TlsUtilities.ReadFully(securityParameters.serverRandom, inStr); - - byte[] sessionID = TlsUtilities.ReadOpaque8(inStr); - if (sessionID.Length > 32) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); - } - - this.tlsClient.NotifySessionID(sessionID); - - /* - * Find out which CipherSuite the server has chosen and check that - * it was one of the offered ones. - */ - int selectedCipherSuite = TlsUtilities.ReadUint16(inStr); - if (!ArrayContains(offeredCipherSuites, selectedCipherSuite) - || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); - } - - this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite); - - /* - * Find out which CompressionMethod the server has chosen and check that - * it was one of the offered ones. - */ - byte selectedCompressionMethod = TlsUtilities.ReadUint8(inStr); - if (!ArrayContains(offeredCompressionMethods, selectedCompressionMethod)) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); - } - - this.tlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod); - - /* - * RFC3546 2.2 The extended server hello message format MAY be - * sent in place of the server hello message when the client has - * requested extended functionality via the extended client hello - * message specified in Section 2.1. - * ... - * Note that the extended server hello message is only sent in response - * to an extended client hello message. This prevents the possibility - * that the extended server hello message could "break" existing TLS 1.0 - * clients. - */ - - /* - * TODO RFC 3546 2.3 - * If [...] the older session is resumed, then the server MUST ignore - * extensions appearing in the client hello, and send a server hello - * containing no extensions. - */ - - // Int32 -> byte[] - IDictionary serverExtensions = Platform.CreateHashtable(); - - if (inStr.Position < inStr.Length) - { - // Process extensions from extended server hello - byte[] extBytes = TlsUtilities.ReadOpaque16(inStr); - - MemoryStream ext = new MemoryStream(extBytes, false); - while (ext.Position < ext.Length) - { - int extType = TlsUtilities.ReadUint16(ext); - byte[] extValue = TlsUtilities.ReadOpaque16(ext); - - // Note: RFC 5746 makes a special case for EXT_RenegotiationInfo - if (extType != ExtensionType.renegotiation_info - && !clientExtensions.Contains(extType)) - { - /* - * RFC 3546 2.3 - * Note that for all extension types (including those defined in - * future), the extension type MUST NOT appear in the extended server - * hello unless the same extension type appeared in the corresponding - * client hello. Thus clients MUST abort the handshake if they receive - * an extension type in the extended server hello that they did not - * request in the associated (extended) client hello. - */ - this.FailWithError(AlertLevel.fatal, AlertDescription.unsupported_extension); - } - - if (serverExtensions.Contains(extType)) - { - /* - * RFC 3546 2.3 - * Also note that when multiple extensions of different types are - * present in the extended client hello or the extended server hello, - * the extensions may appear in any order. There MUST NOT be more than - * one extension of the same type. - */ - this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); - } - - serverExtensions.Add(extType, extValue); - } - } - - AssertEmpty(inStr); - - /* - * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it - * includes the "renegotiation_info" extension: - */ - { - bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info); - - /* - * If the extension is present, set the secure_renegotiation flag - * to TRUE. The client MUST then verify that the length of the - * "renegotiated_connection" field is zero, and if it is not, MUST - * abort the handshake (by sending a fatal handshake_failure - * alert). - */ - if (secure_negotiation) - { - byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info]; - - if (!Arrays.ConstantTimeAreEqual(renegExtValue, - CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - } - } - - tlsClient.NotifySecureRenegotiation(secure_negotiation); - } - - if (clientExtensions != null) - { - tlsClient.ProcessServerExtensions(serverExtensions); - } - - this.keyExchange = tlsClient.GetKeyExchange(); - - connection_state = CS_SERVER_HELLO_RECEIVED; - break; - default: - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; - } - break; - case HandshakeType.server_hello_done: - switch (connection_state) - { - case CS_SERVER_HELLO_RECEIVED: - 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_HELLO_RECEIVED) - { - // There was no server certificate message; check it's OK - this.keyExchange.SkipServerCertificate(); - this.authentication = null; - - // There was no server key exchange message; check it's OK - this.keyExchange.SkipServerKeyExchange(); - } - else if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) - { - // There was no server key exchange message; check it's OK - this.keyExchange.SkipServerKeyExchange(); - } - - AssertEmpty(inStr); - - connection_state = CS_SERVER_HELLO_DONE_RECEIVED; - - TlsCredentials clientCreds = null; - if (certificateRequest == null) - { - this.keyExchange.SkipClientCredentials(); - } - else - { - clientCreds = this.authentication.GetClientCredentials(certificateRequest); - - Certificate clientCert; - if (clientCreds == null) - { - this.keyExchange.SkipClientCredentials(); - clientCert = Certificate.EmptyChain; - } - else - { - this.keyExchange.ProcessClientCredentials(clientCreds); - clientCert = clientCreds.Certificate; - } - - SendClientCertificate(clientCert); - } - - /* - * Send the client key exchange message, depending on the key - * exchange we are using in our CipherSuite. - */ - SendClientKeyExchange(); - - connection_state = CS_CLIENT_KEY_EXCHANGE_SEND; - - if (clientCreds != null && clientCreds is TlsSignerCredentials) - { - TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds; - byte[] md5andsha1 = rs.GetCurrentHash(); - byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature( - md5andsha1); - SendCertificateVerify(clientCertificateSignature); - - connection_state = CS_CERTIFICATE_VERIFY_SEND; - } - - /* - * Now, we send change cipher state - */ - byte[] cmessage = new byte[1]; - cmessage[0] = 1; - rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length); - - connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND; - - /* - * Calculate the master_secret - */ - byte[] pms = this.keyExchange.GeneratePremasterSecret(); - - securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret", - TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), - 48); - - // TODO Is there a way to ensure the data is really overwritten? - /* - * RFC 2246 8.1. The pre_master_secret should be deleted from - * memory once the master_secret has been computed. - */ - Array.Clear(pms, 0, pms.Length); - - /* - * Initialize our cipher suite - */ - rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher()); - - /* - * Send our finished message. - */ - byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret, - "client finished", rs.GetCurrentHash(), 12); - - MemoryStream bos = new MemoryStream(); - TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos); - TlsUtilities.WriteOpaque24(clientVerifyData, bos); - byte[] message = bos.ToArray(); - - rs.WriteMessage(ContentType.handshake, message, 0, message.Length); - - this.connection_state = CS_CLIENT_FINISHED_SEND; - break; - default: - this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - break; - } - break; - case HandshakeType.server_key_exchange: - { - switch (connection_state) - { - case CS_SERVER_HELLO_RECEIVED: - case CS_SERVER_CERTIFICATE_RECEIVED: - { - // NB: Original code used case label fall-through - if (connection_state == CS_SERVER_HELLO_RECEIVED) - { - // There was no server certificate message; check it's OK - this.keyExchange.SkipServerCertificate(); - this.authentication = null; - } - - this.keyExchange.ProcessServerKeyExchange(inStr); - - AssertEmpty(inStr); - break; - } - default: - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; - } - - this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED; - break; - } - case HandshakeType.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 it's OK - this.keyExchange.SkipServerKeyExchange(); - } - - if (this.authentication == null) - { - /* - * RFC 2246 7.4.4. It is a fatal handshake_failure alert - * for an anonymous server to request client identification. - */ - this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - } - - this.certificateRequest = CertificateRequest.Parse(//getContext(), - inStr); - - AssertEmpty(inStr); - - this.keyExchange.ValidateCertificateRequest(this.certificateRequest); - - break; - } - default: - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; - } - - this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED; - break; - case HandshakeType.hello_request: - /* - * RFC 2246 7.4.1.1 Hello request - * This message will be ignored by the client if the client is currently - * negotiating a session. This message may be ignored by the client if it - * does not wish to renegotiate a session, or the client may, if it wishes, - * respond with a no_renegotiation alert. - */ - if (connection_state == CS_DONE) - { - // Renegotiation not supported yet - SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation); - } - break; - case HandshakeType.client_key_exchange: - case HandshakeType.certificate_verify: - case HandshakeType.client_hello: - default: - // We do not support this! - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; - } - } - - 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 = alertQueue.RemoveData(2, 0); - byte level = tmp[0]; - byte description = tmp[1]; - if (level == (byte)AlertLevel.fatal) - { - 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 - { - if (description == (byte)AlertDescription.close_notify) - { - HandleClose(false); - } - - /* - * 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(byte[] buf, int off, int len) - { - for (int i = 0; i < len; ++i) - { - if (buf[off + i] != 1) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.decode_error); - } - - /* - * Check if we are in the correct connection state. - */ - if (this.connection_state != CS_CLIENT_FINISHED_SEND) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } - - rs.ServerClientSpecReceived(); - - this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED; - } - } - - private void SendClientCertificate(Certificate clientCert) - { - MemoryStream bos = new MemoryStream(); - TlsUtilities.WriteUint8((byte)HandshakeType.certificate, bos); - - // Reserve space for length - TlsUtilities.WriteUint24(0, bos); - - clientCert.Encode(bos); - byte[] message = bos.ToArray(); - - // Patch actual length back in - TlsUtilities.WriteUint24(message.Length - 4, message, 1); - - rs.WriteMessage(ContentType.handshake, message, 0, message.Length); - } - - private void SendClientKeyExchange() - { - MemoryStream bos = new MemoryStream(); - TlsUtilities.WriteUint8((byte)HandshakeType.client_key_exchange, bos); - - // Reserve space for length - TlsUtilities.WriteUint24(0, bos); - - this.keyExchange.GenerateClientKeyExchange(bos); - byte[] message = bos.ToArray(); - - // Patch actual length back in - TlsUtilities.WriteUint24(message.Length - 4, message, 1); - - rs.WriteMessage(ContentType.handshake, message, 0, message.Length); - } - - private void SendCertificateVerify(byte[] data) - { - /* - * Send signature of handshake messages so far to prove we are the owner of - * the cert See RFC 2246 sections 4.7, 7.4.3 and 7.4.8 - */ - MemoryStream bos = new MemoryStream(); - TlsUtilities.WriteUint8((byte)HandshakeType.certificate_verify, bos); - TlsUtilities.WriteUint24(data.Length + 2, bos); - TlsUtilities.WriteOpaque16(data, bos); - byte[] message = bos.ToArray(); - - rs.WriteMessage(ContentType.handshake, message, 0, message.Length); - } - - /// <summary>Connects to the remote system.</summary> - /// <param name="verifyer">Will be used when a certificate is received to verify - /// that this certificate is accepted by the client.</param> - /// <exception cref="IOException">If handshake was not successful</exception> - [Obsolete("Use version taking TlsClient")] - public virtual void Connect( - ICertificateVerifyer verifyer) - { - this.Connect(new LegacyTlsClient(verifyer)); - } - - public virtual void Connect(TlsClient tlsClient) - { - if (tlsClient == null) - throw new ArgumentNullException("tlsClient"); - if (this.tlsClient != null) - throw new InvalidOperationException("Connect can only be called once"); - - /* - * Send Client hello - * - * First, generate some random data. - */ - this.securityParameters = new SecurityParameters(); - this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), random, - ExporterLabel.client_random); - - this.tlsClientContext = new TlsClientContextImpl(random, securityParameters); - this.tlsClient = tlsClient; - this.tlsClient.Init(tlsClientContext); - - MemoryStream outStr = new MemoryStream(); - TlsUtilities.WriteVersion(outStr); - outStr.Write(securityParameters.clientRandom, 0, 32); - - /* - * Length of Session id - */ - TlsUtilities.WriteUint8(0, outStr); - - this.offeredCipherSuites = this.tlsClient.GetCipherSuites(); - - // Int32 -> byte[] - this.clientExtensions = this.tlsClient.GetClientExtensions(); - - // Cipher Suites (and SCSV) - { - /* - * RFC 5746 3.4. - * The client MUST include either an empty "renegotiation_info" - * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling - * cipher suite value in the ClientHello. Including both is NOT - * RECOMMENDED. - */ - bool noRenegExt = clientExtensions == null - || !clientExtensions.Contains(ExtensionType.renegotiation_info); - - int count = offeredCipherSuites.Length; - if (noRenegExt) - { - // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV - ++count; - } - - TlsUtilities.WriteUint16(2 * count, outStr); - - for (int i = 0; i < offeredCipherSuites.Length; ++i) - { - TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr); - } - - if (noRenegExt) - { - TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr); - } - } - - /* - * Compression methods, just the null method. - */ - this.offeredCompressionMethods = tlsClient.GetCompressionMethods(); - - { - TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr); - for (int i = 0; i < offeredCompressionMethods.Length; ++i) - { - TlsUtilities.WriteUint8(offeredCompressionMethods[i], outStr); - } - } - - // Extensions - if (clientExtensions != null) - { - MemoryStream ext = new MemoryStream(); - - foreach (int extType in clientExtensions.Keys) - { - WriteExtension(ext, extType, (byte[])clientExtensions[extType]); - } - - TlsUtilities.WriteOpaque16(ext.ToArray(), outStr); - } - - MemoryStream bos = new MemoryStream(); - TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos); - TlsUtilities.WriteUint24((int)outStr.Length, bos); - byte[] outBytes = outStr.ToArray(); - bos.Write(outBytes, 0, outBytes.Length); - byte[] message = bos.ToArray(); - SafeWriteMessage(ContentType.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) - { - SafeReadData(); - } - - this.tlsStream = new TlsStream(this); - } - - /** - * Read data from the network. The method will return immediately, if there is - * still some data left in the buffer, or block until some application - * data has been read from the network. - * - * @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) - { - if (this.closed) - { - /* - * We need to read some data. - */ - if (this.failedWithError) - { - /* - * Something went terribly wrong, we should throw an IOException - */ - throw new IOException(TLS_ERROR_MESSAGE); - } - - /* - * Connection has been closed, there is no more data to read. - */ - return 0; - } - - SafeReadData(); - } - len = System.Math.Min(len, applicationDataQueue.Available); - applicationDataQueue.RemoveData(buf, offset, len, 0); - return len; - } - - private void SafeReadData() - { - try - { - rs.ReadData(); - } - catch (TlsFatalAlert e) - { - if (!this.closed) - { - this.FailWithError(AlertLevel.fatal, e.AlertDescription); - } - throw e; - } - catch (IOException e) - { - if (!this.closed) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error); - } - throw e; - } - catch (Exception e) - { - if (!this.closed) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error); - } - throw e; - } - } - - private void SafeWriteMessage(byte type, byte[] buf, int offset, int len) - { - try - { - rs.WriteMessage(type, buf, offset, len); - } - catch (TlsFatalAlert e) - { - if (!this.closed) - { - this.FailWithError(AlertLevel.fatal, e.AlertDescription); - } - throw e; - } - catch (IOException e) - { - if (!closed) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error); - } - throw e; - } - catch (Exception e) - { - if (!closed) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error); - } - throw e; - } - } - - /** - * Send some application data to the remote system. - * <p/> - * 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.closed) - { - if (this.failedWithError) - throw new IOException(TLS_ERROR_MESSAGE); - - throw new IOException("Sorry, connection has been closed, you cannot write more data"); - } - - while (len > 0) - { - /* - * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are - * potentially useful as a traffic analysis countermeasure. - * - * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting. - */ - - //if (this.splitApplicationDataRecords) - { - /* - * Protect against known IV attack! - * - * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. - */ - SafeWriteMessage(ContentType.application_data, buf, offset, 1); - ++offset; - --len; - } - - if (len > 0) - { - // Fragment data according to the current fragment limit. - //int toWrite = System.Math.Min(len, recordStream.GetPlaintextLimit()); - int toWrite = System.Math.Min(len, 1 << 14); - SafeWriteMessage(ContentType.application_data, buf, offset, toWrite); - offset += toWrite; - len -= toWrite; - } - } - } - - /// <summary>A Stream which can be used to send data.</summary> - [Obsolete("Use 'Stream' property instead")] - public virtual Stream OutputStream - { - get { return this.tlsStream; } - } - - /// <summary>A Stream which can be used to read data.</summary> - [Obsolete("Use 'Stream' property instead")] - public virtual Stream InputStream - { - get { return this.tlsStream; } - } - - /// <summary>The secure bidirectional stream for this connection</summary> - public virtual Stream Stream - { - get { return this.tlsStream; } - } - - /** - * Terminate this connection with an alert. - * <p/> - * Can be used for normal closure too. - * - * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning. - * @param alertDescription The exact alert message. - * @throws IOException If alert was fatal. - */ - private void FailWithError(byte alertLevel, byte alertDescription) - { - /* - * Check if the connection is still open. - */ - if (!closed) - { - /* - * Prepare the message - */ - this.closed = true; - - if (alertLevel == AlertLevel.fatal) - { - /* - * This is a fatal message. - */ - this.failedWithError = true; - } - SendAlert(alertLevel, alertDescription); - rs.Close(); - if (alertLevel == AlertLevel.fatal) - { - throw new IOException(TLS_ERROR_MESSAGE); - } - } - else - { - throw new IOException(TLS_ERROR_MESSAGE); - } - } - - internal void SendAlert(byte alertLevel, byte alertDescription) - { - byte[] error = new byte[] { alertLevel, alertDescription }; - - rs.WriteMessage(ContentType.alert, error, 0, 2); - } - - /// <summary>Closes this connection</summary> - /// <exception cref="IOException">If something goes wrong during closing.</exception> - public virtual void Close() - { - HandleClose(true); - } - - protected virtual void HandleClose(bool user_canceled) - { - if (!closed) - { - if (user_canceled && !appDataReady) - { - SendAlert(AlertLevel.warning, AlertDescription.user_canceled); - } - this.FailWithError(AlertLevel.warning, AlertDescription.close_notify); - } - } - - /** - * 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.Position < inStr.Length) - { - throw new TlsFatalAlert(AlertDescription.decode_error); - } - } - - protected static byte[] CreateRandomBlock(bool useGMTUnixTime, SecureRandom random, string asciiLabel) - { - /* - * We use the TLS 1.0 PRF on the SecureRandom output, to guard against RNGs where the raw - * output could be used to recover the internal state. - */ - byte[] secret = new byte[32]; - random.NextBytes(secret); - - byte[] seed = new byte[8]; - // TODO Use high-resolution timer - TlsUtilities.WriteUint64(DateTimeUtilities.CurrentUnixMs(), seed, 0); - - byte[] result = TlsUtilities.PRF(secret, asciiLabel, seed, 32); - - if (useGMTUnixTime) - { - TlsUtilities.WriteGmtUnixTime(result, 0); - } - - return result; - } - - internal void Flush() - { - rs.Flush(); - } - - internal bool IsClosed - { - get { return closed; } - } - - private static bool ArrayContains(byte[] a, byte n) - { - for (int i = 0; i < a.Length; ++i) - { - if (a[i] == n) - return true; - } - return false; - } - - private static bool ArrayContains(int[] a, int n) - { - for (int i = 0; i < a.Length; ++i) - { - if (a[i] == n) - return true; - } - return false; - } - - private static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection) - { - MemoryStream buf = new MemoryStream(); - TlsUtilities.WriteOpaque8(renegotiated_connection, buf); - return buf.ToArray(); - } - - private static void WriteExtension(Stream output, int extType, byte[] extValue) + public TlsProtocolHandler(Stream input, Stream output, SecureRandom secureRandom) + : base(input, output, secureRandom) { - TlsUtilities.WriteUint16(extType, output); - TlsUtilities.WriteOpaque16(extValue, output); } } } diff --git a/crypto/src/crypto/tls/TlsPskIdentityManager.cs b/crypto/src/crypto/tls/TlsPskIdentityManager.cs new file mode 100644
index 000000000..a72c2299c --- /dev/null +++ b/crypto/src/crypto/tls/TlsPskIdentityManager.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsPskIdentityManager + { + byte[] GetHint(); + + byte[] GetPsk(byte[] identity); + } +} diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
index 9961fc9d1..0af7f7a69 100644 --- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -1,190 +1,314 @@ using System; +using System.Collections; using System.IO; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsPskKeyExchange - : TlsKeyExchange + /// <summary>(D)TLS PSK key exchange (RFC 4279).</summary> + public class TlsPskKeyExchange + : AbstractTlsKeyExchange { - protected TlsClientContext context; - protected int keyExchange; - protected TlsPskIdentity pskIdentity; + protected TlsPskIdentity mPskIdentity; + protected TlsPskIdentityManager mPskIdentityManager; - protected byte[] psk_identity_hint = null; + protected DHParameters mDHParameters; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; - protected DHPublicKeyParameters dhAgreeServerPublicKey = null; - protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null; + protected byte[] mPskIdentityHint = null; + protected byte[] mPsk = null; - protected AsymmetricKeyParameter serverPublicKey = null; - protected RsaKeyParameters rsaServerPublicKey = null; - protected byte[] premasterSecret; + protected DHPrivateKeyParameters mDHAgreePrivateKey = null; + protected DHPublicKeyParameters mDHAgreePublicKey = null; - internal TlsPskKeyExchange(TlsClientContext context, int keyExchange, - TlsPskIdentity pskIdentity) + protected ECPrivateKeyParameters mECAgreePrivateKey = null; + protected ECPublicKeyParameters mECAgreePublicKey = null; + + protected AsymmetricKeyParameter mServerPublicKey = null; + protected RsaKeyParameters mRsaServerPublicKey = null; + protected TlsEncryptionCredentials mServerCredentials = null; + protected byte[] mPremasterSecret; + + public TlsPskKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsPskIdentity pskIdentity, + TlsPskIdentityManager pskIdentityManager, DHParameters dhParameters, int[] namedCurves, + byte[] clientECPointFormats, byte[] serverECPointFormats) + : base(keyExchange, supportedSignatureAlgorithms) { switch (keyExchange) { - case KeyExchangeAlgorithm.PSK: - case KeyExchangeAlgorithm.RSA_PSK: - case KeyExchangeAlgorithm.DHE_PSK: - break; - default: - throw new ArgumentException("unsupported key exchange algorithm", "keyExchange"); + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.RSA_PSK: + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); } - this.context = context; - this.keyExchange = keyExchange; - this.pskIdentity = pskIdentity; + this.mPskIdentity = pskIdentity; + this.mPskIdentityManager = pskIdentityManager; + this.mDHParameters = dhParameters; + this.mNamedCurves = namedCurves; + this.mClientECPointFormats = clientECPointFormats; + this.mServerECPointFormats = serverECPointFormats; } - public virtual void SkipServerCertificate() + public override void SkipServerCredentials() { - if (keyExchange == KeyExchangeAlgorithm.RSA_PSK) - { + if (mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) throw new TlsFatalAlert(AlertDescription.unexpected_message); - } } - public virtual void ProcessServerCertificate(Certificate serverCertificate) + public override void ProcessServerCredentials(TlsCredentials serverCredentials) { - if (keyExchange != KeyExchangeAlgorithm.RSA_PSK) + if (!(serverCredentials is TlsEncryptionCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsEncryptionCredentials)serverCredentials; + } + + public override byte[] GenerateServerKeyExchange() + { + this.mPskIdentityHint = mPskIdentityManager.GetHint(); + + if (this.mPskIdentityHint == null && !RequiresServerKeyExchange) + return null; + + MemoryStream buf = new MemoryStream(); + + if (this.mPskIdentityHint == null) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + TlsUtilities.WriteOpaque16(TlsUtilities.EmptyBytes, buf); + } + else + { + TlsUtilities.WriteOpaque16(this.mPskIdentityHint, buf); } + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + if (this.mDHParameters == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom, + this.mDHParameters, buf); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom, + mNamedCurves, mClientECPointFormats, buf); + } + + return buf.ToArray(); + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (mKeyExchange != KeyExchangeAlgorithm.RSA_PSK) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); - SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; try { - this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); } - // catch (RuntimeException) - catch (Exception) + catch (Exception e) { - throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); } // Sanity check the PublicKeyFactory - if (this.serverPublicKey.IsPrivate) - { + if (this.mServerPublicKey.IsPrivate) throw new TlsFatalAlert(AlertDescription.internal_error); - } - this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey); + this.mRsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.mServerPublicKey); TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment); - // TODO - /* - * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the - * signing algorithm for the certificate must be the same as the algorithm for the - * certificate key." - */ + base.ProcessServerCertificate(serverCertificate); } - public virtual void SkipServerKeyExchange() + public override bool RequiresServerKeyExchange { - if (keyExchange == KeyExchangeAlgorithm.DHE_PSK) + get { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + return true; + default: + return false; + } } - - this.psk_identity_hint = TlsUtilities.EmptyBytes; } - public virtual void ProcessServerKeyExchange(Stream input) + public override void ProcessServerKeyExchange(Stream input) { - this.psk_identity_hint = TlsUtilities.ReadOpaque16(input); + this.mPskIdentityHint = TlsUtilities.ReadOpaque16(input); - if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) { - byte[] pBytes = TlsUtilities.ReadOpaque16(input); - byte[] gBytes = TlsUtilities.ReadOpaque16(input); - byte[] YsBytes = TlsUtilities.ReadOpaque16(input); + ServerDHParams serverDHParams = ServerDHParams.Parse(input); - BigInteger p = new BigInteger(1, pBytes); - BigInteger g = new BigInteger(1, gBytes); - BigInteger Ys = new BigInteger(1, YsBytes); - - this.dhAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey( - new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(serverDHParams.PublicKey); + this.mDHParameters = mDHAgreePublicKey.Parameters; } - else if (this.psk_identity_hint.Length == 0) + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - // TODO Should we enforce that this message should have been skipped if hint is empty? - //throw new TlsFatalAlert(AlertDescription.unexpected_message); + ECDomainParameters ecParams = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, input); + + byte[] point = TlsUtilities.ReadOpaque8(input); + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mClientECPointFormats, ecParams, point)); } } - public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public virtual void SkipClientCredentials() - { - // OK - } - - public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { throw new TlsFatalAlert(AlertDescription.internal_error); } - public virtual void GenerateClientKeyExchange(Stream output) + public override void GenerateClientKeyExchange(Stream output) { - if (psk_identity_hint == null || psk_identity_hint.Length == 0) + if (mPskIdentityHint == null) { - pskIdentity.SkipIdentityHint(); + mPskIdentity.SkipIdentityHint(); } else { - pskIdentity.NotifyIdentityHint(psk_identity_hint); + mPskIdentity.NotifyIdentityHint(mPskIdentityHint); } - byte[] psk_identity = pskIdentity.GetPskIdentity(); + byte[] psk_identity = mPskIdentity.GetPskIdentity(); + if (psk_identity == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.mPsk = mPskIdentity.GetPsk(); + if (mPsk == null) + throw new TlsFatalAlert(AlertDescription.internal_error); TlsUtilities.WriteOpaque16(psk_identity, output); - if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + mContext.SecurityParameters.pskIdentity = psk_identity; + + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom, + mDHParameters, output); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom, + mServerECPointFormats, mECAgreePublicKey.Parameters, output); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(mContext, + this.mRsaServerPublicKey, output); + } + } + + public override void ProcessClientKeyExchange(Stream input) + { + byte[] psk_identity = TlsUtilities.ReadOpaque16(input); + + this.mPsk = mPskIdentityManager.GetPsk(psk_identity); + if (mPsk == null) + throw new TlsFatalAlert(AlertDescription.unknown_psk_identity); + + mContext.SecurityParameters.pskIdentity = psk_identity; + + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + BigInteger Yc = TlsDHUtilities.ReadDHParameter(input); + + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Yc, mDHParameters)); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( - context.SecureRandom, this.rsaServerPublicKey, output); + byte[] point = TlsUtilities.ReadOpaque8(input); + + ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters; + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mServerECPointFormats, curve_params, point)); } - else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) { - this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange( - context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output); + byte[] encryptedPreMasterSecret; + if (TlsUtilities.IsSsl(mContext)) + { + // TODO Do any SSLv3 clients actually include the length? + encryptedPreMasterSecret = Streams.ReadAll(input); + } + else + { + encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input); + } + + this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret); } } - public virtual byte[] GeneratePremasterSecret() + public override byte[] GeneratePremasterSecret() { - byte[] psk = pskIdentity.GetPsk(); - byte[] other_secret = GenerateOtherSecret(psk.Length); + byte[] other_secret = GenerateOtherSecret(mPsk.Length); - MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length); + MemoryStream buf = new MemoryStream(4 + other_secret.Length + mPsk.Length); TlsUtilities.WriteOpaque16(other_secret, buf); - TlsUtilities.WriteOpaque16(psk, buf); + TlsUtilities.WriteOpaque16(mPsk, buf); + + Arrays.Fill(mPsk, (byte)0); + this.mPsk = null; + return buf.ToArray(); } protected virtual byte[] GenerateOtherSecret(int pskLength) { - if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) { - return TlsDHUtilities.CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); + if (mDHAgreePrivateKey != null) + { + return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreePublicKey, mDHAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); } - if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - return this.premasterSecret; + if (mECAgreePrivateKey != null) + { + return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + return this.mPremasterSecret; } return new byte[pskLength]; @@ -193,12 +317,10 @@ namespace Org.BouncyCastle.Crypto.Tls protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key) { // TODO What is the minimum bit length required? - // key.Modulus.BitLength; + // key.Modulus.BitLength; if (!key.Exponent.IsProbablePrime(2)) - { throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } return key; } diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index aad482316..b02d56486 100644 --- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -1,163 +1,138 @@ 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.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// TLS 1.0 RSA key exchange. - /// </summary> - internal class TlsRsaKeyExchange - : TlsKeyExchange + /// <summary>(D)TLS and SSLv3 RSA key exchange.</summary> + public class TlsRsaKeyExchange + : AbstractTlsKeyExchange { - protected TlsClientContext context; + protected AsymmetricKeyParameter mServerPublicKey = null; - protected AsymmetricKeyParameter serverPublicKey = null; + protected RsaKeyParameters mRsaServerPublicKey = null; - protected RsaKeyParameters rsaServerPublicKey = null; + protected TlsEncryptionCredentials mServerCredentials = null; - protected byte[] premasterSecret; + protected byte[] mPremasterSecret; - internal TlsRsaKeyExchange(TlsClientContext context) + public TlsRsaKeyExchange(IList supportedSignatureAlgorithms) + : base(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms) { - this.context = context; } - public virtual void SkipServerCertificate() + public override void SkipServerCredentials() { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public virtual void ProcessServerCertificate(Certificate serverCertificate) + public override void ProcessServerCredentials(TlsCredentials serverCredentials) { + if (!(serverCredentials is TlsEncryptionCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsEncryptionCredentials)serverCredentials; + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); - SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; try { - this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); } -// catch (RuntimeException) - catch (Exception) + catch (Exception e) { - throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); } // Sanity check the PublicKeyFactory - if (this.serverPublicKey.IsPrivate) - { + if (this.mServerPublicKey.IsPrivate) throw new TlsFatalAlert(AlertDescription.internal_error); - } - this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey); + this.mRsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.mServerPublicKey); TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment); - // TODO - /* - * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the - * signing algorithm for the certificate must be the same as the algorithm for the - * certificate key." - */ - } - - public virtual void SkipServerKeyExchange() - { - // OK - } - - public virtual void ProcessServerKeyExchange(Stream input) - { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + base.ProcessServerCertificate(serverCertificate); } - public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { byte[] types = certificateRequest.CertificateTypes; - foreach (byte type in types) + for (int i = 0; i < types.Length; ++i) { - switch (type) + switch (types[i]) { - case ClientCertificateType.rsa_sign: - case ClientCertificateType.dss_sign: - case ClientCertificateType.ecdsa_sign: - break; - default: - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } - public virtual void SkipClientCredentials() + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { - // OK + if (!(clientCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); } - public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + public override void GenerateClientKeyExchange(Stream output) { - if (!(clientCredentials is TlsSignerCredentials)) - { - throw new TlsFatalAlert(AlertDescription.internal_error); - } + this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(mContext, mRsaServerPublicKey, output); } - - public virtual void GenerateClientKeyExchange(Stream output) + + public override void ProcessClientKeyExchange(Stream input) { - this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( - context.SecureRandom, this.rsaServerPublicKey, output); + byte[] encryptedPreMasterSecret; + if (TlsUtilities.IsSsl(mContext)) + { + // TODO Do any SSLv3 clients actually include the length? + encryptedPreMasterSecret = Streams.ReadAll(input); + } + else + { + encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input); + } + + this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret); } - public virtual byte[] GeneratePremasterSecret() + public override byte[] GeneratePremasterSecret() { - byte[] tmp = this.premasterSecret; - this.premasterSecret = null; + if (this.mPremasterSecret == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + byte[] tmp = this.mPremasterSecret; + this.mPremasterSecret = null; return tmp; } - // Would be needed to process RSA_EXPORT server key exchange -// protected virtual void ProcessRsaServerKeyExchange(Stream input, ISigner signer) -// { -// Stream sigIn = input; -// if (signer != null) -// { -// sigIn = new SignerStream(input, signer, null); -// } -// -// byte[] modulusBytes = TlsUtilities.ReadOpaque16(sigIn); -// byte[] exponentBytes = TlsUtilities.ReadOpaque16(sigIn); -// -// if (signer != null) -// { -// byte[] sigByte = TlsUtilities.ReadOpaque16(input); -// -// if (!signer.VerifySignature(sigByte)) -// { -// handler.FailWithError(AlertLevel.fatal, AlertDescription.decrypt_error); -// } -// } -// -// BigInteger modulus = new BigInteger(1, modulusBytes); -// BigInteger exponent = new BigInteger(1, exponentBytes); -// -// this.rsaServerPublicKey = ValidateRSAPublicKey(new RsaKeyParameters(false, modulus, exponent)); -// } - protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key) { // TODO What is the minimum bit length required? -// key.Modulus.BitLength; + // key.Modulus.BitLength; if (!key.Exponent.IsProbablePrime(2)) - { throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } return key; } diff --git a/crypto/src/crypto/tls/TlsRsaSigner.cs b/crypto/src/crypto/tls/TlsRsaSigner.cs
index ce18ef5e1..6da1c5e9b 100644 --- a/crypto/src/crypto/tls/TlsRsaSigner.cs +++ b/crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -10,50 +10,92 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsRsaSigner - : TlsSigner + public class TlsRsaSigner + : AbstractTlsSigner { - public virtual byte[] GenerateRawSignature(SecureRandom random, - AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) { - IAsymmetricBlockCipher engine = CreateRsaImpl(); - engine.Init(true, new ParametersWithRandom(privateKey, random)); - return engine.ProcessBlock(md5AndSha1, 0, md5AndSha1.Length); + ISigner signer = MakeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + signer.BlockUpdate(hash, 0, hash.Length); + return signer.GenerateSignature(); } - public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, - byte[] md5AndSha1) + public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) { - IAsymmetricBlockCipher engine = CreateRsaImpl(); - engine.Init(false, publicKey); - byte[] signed = engine.ProcessBlock(sigBytes, 0, sigBytes.Length); - return Arrays.ConstantTimeAreEqual(signed, md5AndSha1); + ISigner signer = MakeSigner(algorithm, true, false, publicKey); + signer.BlockUpdate(hash, 0, hash.Length); + return signer.VerifySignature(sigBytes); } - public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey) + public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) { - return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random)); + return MakeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); } - public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) { - return MakeSigner(new CombinedHash(), false, publicKey); + return MakeSigner(algorithm, false, false, publicKey); } - public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) { return publicKey is RsaKeyParameters && !publicKey.IsPrivate; } - protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp) + protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning, + ICipherParameters cp) { - ISigner s = new GenericSigner(CreateRsaImpl(), d); + if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext)) + throw new InvalidOperationException(); + if (algorithm != null && algorithm.Signature != SignatureAlgorithm.rsa) + throw new InvalidOperationException(); + + IDigest d; + if (raw) + { + d = new NullDigest(); + } + else if (algorithm == null) + { + d = new CombinedHash(); + } + else + { + d = TlsUtilities.CreateHash(algorithm.Hash); + } + + ISigner s; + if (algorithm != null) + { + /* + * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated + * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1]. + */ + s = new RsaDigestSigner(d, TlsUtilities.GetOidForHashAlgorithm(algorithm.Hash)); + } + else + { + /* + * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme + * that did not include a DigestInfo encoding. + */ + s = new GenericSigner(CreateRsaImpl(), d); + } s.Init(forSigning, cp); return s; } protected virtual IAsymmetricBlockCipher CreateRsaImpl() { + /* + * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks + * on TLS are possible, at least when the client and server are on the same LAN. + * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other + * anti-timing technique, as described in [TIMING]. + */ return new Pkcs1Encoding(new RsaBlindedEngine()); } } diff --git a/crypto/src/crypto/tls/TlsRsaUtilities.cs b/crypto/src/crypto/tls/TlsRsaUtilities.cs
index 4450ba452..0e42c1733 100644 --- a/crypto/src/crypto/tls/TlsRsaUtilities.cs +++ b/crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -5,38 +5,128 @@ using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public abstract class TlsRsaUtilities - { - public static byte[] GenerateEncryptedPreMasterSecret(SecureRandom random, - RsaKeyParameters rsaServerPublicKey, Stream output) - { - /* - * Choose a PremasterSecret and send it encrypted to the server - */ - byte[] premasterSecret = new byte[48]; - random.NextBytes(premasterSecret); - TlsUtilities.WriteVersion(premasterSecret, 0); - - Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine()); - encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, random)); - - try - { - byte[] keData = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length); - TlsUtilities.WriteOpaque16(keData, output); - } - catch (InvalidCipherTextException) - { - /* - * This should never happen, only during decryption. - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - - return premasterSecret; - } - } + public abstract class TlsRsaUtilities + { + /// <exception cref="IOException"></exception> + public static byte[] GenerateEncryptedPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPublicKey, + Stream output) + { + /* + * Choose a PremasterSecret and send it encrypted to the server + */ + byte[] premasterSecret = new byte[48]; + context.SecureRandom.NextBytes(premasterSecret); + TlsUtilities.WriteVersion(context.ClientVersion, premasterSecret, 0); + + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine()); + encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, context.SecureRandom)); + + try + { + byte[] encryptedPreMasterSecret = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length); + + if (TlsUtilities.IsSsl(context)) + { + // TODO Do any SSLv3 servers actually expect the length? + output.Write(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); + } + else + { + TlsUtilities.WriteOpaque16(encryptedPreMasterSecret, output); + } + } + catch (InvalidCipherTextException e) + { + /* + * This should never happen, only during decryption. + */ + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + + return premasterSecret; + } + + public static byte[] SafeDecryptPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPrivateKey, + byte[] encryptedPreMasterSecret) + { + /* + * RFC 5246 7.4.7.1. + */ + ProtocolVersion clientVersion = context.ClientVersion; + + // TODO Provide as configuration option? + bool versionNumberCheckDisabled = false; + + /* + * Generate 48 random bytes we can use as a Pre-Master-Secret, if the + * PKCS1 padding check should fail. + */ + byte[] fallback = new byte[48]; + context.SecureRandom.NextBytes(fallback); + + byte[] M = Arrays.Clone(fallback); + try + { + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback); + encoding.Init(false, + new ParametersWithRandom(rsaServerPrivateKey, context.SecureRandom)); + + M = encoding.ProcessBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); + } + catch (Exception) + { + /* + * This should never happen since the decryption should never throw an exception + * and return a random value instead. + * + * In any case, a TLS server MUST NOT generate an alert if processing an + * RSA-encrypted premaster secret message fails, or the version number is not as + * expected. Instead, it MUST continue the handshake with a randomly generated + * premaster secret. + */ + } + + /* + * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST + * check the version number [..]. + */ + if (versionNumberCheckDisabled && clientVersion.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv10)) + { + /* + * If the version number is TLS 1.0 or earlier, server + * implementations SHOULD check the version number, but MAY have a + * configuration option to disable the check. + * + * So there is nothing to do here. + */ + } + else + { + /* + * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the + * clientVersion received during the handshake. If they don't match, we replace the + * decrypted Pre-Master-Secret with a random one. + */ + int correct = (clientVersion.MajorVersion ^ (M[0] & 0xff)) + | (clientVersion.MinorVersion ^ (M[1] & 0xff)); + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + int mask = ~((correct & 1) - 1); + + /* + * mask will be all bits set to 0xff if the version number differed. + */ + for (int i = 0; i < 48; i++) + { + M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask)); + } + } + return M; + } + } } diff --git a/crypto/src/crypto/tls/TlsServer.cs b/crypto/src/crypto/tls/TlsServer.cs new file mode 100644
index 000000000..e791f93a9 --- /dev/null +++ b/crypto/src/crypto/tls/TlsServer.cs
@@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsServer + : TlsPeer + { + void Init(TlsServerContext context); + + /// <exception cref="IOException"></exception> + void NotifyClientVersion(ProtocolVersion clientVersion); + + /// <exception cref="IOException"></exception> + void NotifyFallback(bool isFallback); + + /// <exception cref="IOException"></exception> + void NotifyOfferedCipherSuites(int[] offeredCipherSuites); + + /// <exception cref="IOException"></exception> + void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods); + + /// <param name="clientExtensions">A <see cref="IDictionary"/> (Int32 -> byte[]). Will never be null.</param> + /// <exception cref="IOException"></exception> + void ProcessClientExtensions(IDictionary clientExtensions); + + /// <exception cref="IOException"></exception> + ProtocolVersion GetServerVersion(); + + /// <exception cref="IOException"></exception> + int GetSelectedCipherSuite(); + + /// <exception cref="IOException"></exception> + byte GetSelectedCompressionMethod(); + + /// <summary> + /// Get the (optional) table of server extensions to be included in (extended) server hello. + /// </summary> + /// <returns> + /// A <see cref="IDictionary"/> (Int32 -> byte[]). May be null. + /// </returns> + /// <exception cref="IOException"></exception> + IDictionary GetServerExtensions(); + + /// <returns> + /// A <see cref="IList"/> (<see cref="SupplementalDataEntry"/>). May be null. + /// </returns> + /// <exception cref="IOException"></exception> + IList GetServerSupplementalData(); + + /// <exception cref="IOException"></exception> + TlsCredentials GetCredentials(); + + /// <remarks> + /// This method will be called (only) if the server included an extension of type + /// "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546 + /// 3.6. Certificate Status Request</i>. If a non-null <see cref="CertificateStatus"/> is returned, it + /// is sent to the client as a handshake message of type "certificate_status". + /// </remarks> + /// <returns>A <see cref="CertificateStatus"/> to be sent to the client (or null for none).</returns> + /// <exception cref="IOException"></exception> + CertificateStatus GetCertificateStatus(); + + /// <exception cref="IOException"></exception> + TlsKeyExchange GetKeyExchange(); + + /// <exception cref="IOException"></exception> + CertificateRequest GetCertificateRequest(); + + /// <param name="clientSupplementalData"><see cref="IList"/> (<see cref="SupplementalDataEntry"/>)</param> + /// <exception cref="IOException"></exception> + void ProcessClientSupplementalData(IList clientSupplementalData); + + /// <summary> + /// Called by the protocol handler to report the client certificate, only if <c>GetCertificateRequest</c> + /// returned non-null. + /// </summary> + /// <remarks>Note: this method is responsible for certificate verification and validation.</remarks> + /// <param name="clientCertificate">the effective client certificate (may be an empty chain).</param> + /// <exception cref="IOException"></exception> + void NotifyClientCertificate(Certificate clientCertificate); + + /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message.</summary> + /// <remarks> + /// This method will be called (only) if a NewSessionTicket extension was sent by the server. See + /// <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection. + /// </remarks> + /// <returns>The <see cref="NewSessionTicket">ticket</see>)</returns> + /// <exception cref="IOException"></exception> + NewSessionTicket GetNewSessionTicket(); + } +} diff --git a/crypto/src/crypto/tls/TlsServerContext.cs b/crypto/src/crypto/tls/TlsServerContext.cs new file mode 100644
index 000000000..4021571aa --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerContext.cs
@@ -0,0 +1,11 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsServerContext + : TlsContext + { + } +} diff --git a/crypto/src/crypto/tls/TlsServerContextImpl.cs b/crypto/src/crypto/tls/TlsServerContextImpl.cs new file mode 100644
index 000000000..d56566ffc --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerContextImpl.cs
@@ -0,0 +1,20 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsServerContextImpl + : AbstractTlsContext, TlsServerContext + { + internal TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + : base(secureRandom, securityParameters) + { + } + + public override bool IsServer + { + get { return true; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs new file mode 100644
index 000000000..27f7a1dfd --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -0,0 +1,803 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsServerProtocol + : TlsProtocol + { + protected TlsServer mTlsServer = null; + internal TlsServerContextImpl mTlsServerContext = null; + + protected TlsKeyExchange mKeyExchange = null; + protected TlsCredentials mServerCredentials = null; + protected CertificateRequest mCertificateRequest = null; + + protected short mClientCertificateType = -1; + protected TlsHandshakeHash mPrepareFinishHash = null; + + /** + * Constructor for blocking mode. + * @param stream The bi-directional stream of data to/from the client + * @param output The stream of data to the client + * @param secureRandom Random number generator for various cryptographic functions + */ + public TlsServerProtocol(Stream stream, SecureRandom secureRandom) + : base(stream, secureRandom) + { + } + + /** + * Constructor for blocking mode. + * @param input The stream of data from the client + * @param output The stream of data to the client + * @param secureRandom Random number generator for various cryptographic functions + */ + public TlsServerProtocol(Stream input, Stream output, SecureRandom secureRandom) + : base(input, output, secureRandom) + { + } + + /** + * Constructor for non-blocking mode.<br> + * <br> + * When data is received, use {@link #offerInput(java.nio.ByteBuffer)} to + * provide the received ciphertext, then use + * {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br> + * <br> + * Similarly, when data needs to be sent, use + * {@link #offerOutput(byte[], int, int)} to provide the cleartext, then use + * {@link #readOutput(byte[], int, int)} to get the corresponding + * ciphertext. + * + * @param secureRandom + * Random number generator for various cryptographic functions + */ + public TlsServerProtocol(SecureRandom secureRandom) + : base(secureRandom) + { + } + + /** + * Receives a TLS handshake in the role of server.<br> + * <br> + * In blocking mode, this will not return until the handshake is complete. + * In non-blocking mode, use {@link TlsPeer#notifyHandshakeComplete()} to + * receive a callback when the handshake is complete. + * + * @param tlsServer + * @throws IOException If in blocking mode and handshake was not successful. + */ + public virtual void Accept(TlsServer tlsServer) + { + if (tlsServer == null) + throw new ArgumentNullException("tlsServer"); + if (this.mTlsServer != null) + throw new InvalidOperationException("'Accept' can only be called once"); + + this.mTlsServer = tlsServer; + + this.mSecurityParameters = new SecurityParameters(); + this.mSecurityParameters.entity = ConnectionEnd.server; + + this.mTlsServerContext = new TlsServerContextImpl(mSecureRandom, mSecurityParameters); + + this.mSecurityParameters.serverRandom = CreateRandomBlock(tlsServer.ShouldUseGmtUnixTime(), + mTlsServerContext.NonceRandomGenerator); + + this.mTlsServer.Init(mTlsServerContext); + this.mRecordStream.Init(mTlsServerContext); + + this.mRecordStream.SetRestrictReadVersion(false); + + BlockForHandshake(); + } + + protected override void CleanupHandshake() + { + base.CleanupHandshake(); + + this.mKeyExchange = null; + this.mServerCredentials = null; + this.mCertificateRequest = null; + this.mPrepareFinishHash = null; + } + + protected override TlsContext Context + { + get { return mTlsServerContext; } + } + + internal override AbstractTlsContext ContextAdmin + { + get { return mTlsServerContext; } + } + + protected override TlsPeer Peer + { + get { return mTlsServer; } + } + + protected override void HandleHandshakeMessage(byte type, byte[] data) + { + MemoryStream buf = new MemoryStream(data); + + switch (type) + { + case HandshakeType.client_hello: + { + switch (this.mConnectionState) + { + case CS_START: + { + ReceiveClientHelloMessage(buf); + this.mConnectionState = CS_CLIENT_HELLO; + + SendServerHelloMessage(); + this.mConnectionState = CS_SERVER_HELLO; + + mRecordStream.NotifyHelloComplete(); + + IList serverSupplementalData = mTlsServer.GetServerSupplementalData(); + if (serverSupplementalData != null) + { + SendSupplementalDataMessage(serverSupplementalData); + } + this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA; + + this.mKeyExchange = mTlsServer.GetKeyExchange(); + this.mKeyExchange.Init(Context); + + this.mServerCredentials = mTlsServer.GetCredentials(); + + Certificate serverCertificate = null; + + if (this.mServerCredentials == null) + { + this.mKeyExchange.SkipServerCredentials(); + } + else + { + this.mKeyExchange.ProcessServerCredentials(this.mServerCredentials); + + serverCertificate = this.mServerCredentials.Certificate; + SendCertificateMessage(serverCertificate); + } + this.mConnectionState = CS_SERVER_CERTIFICATE; + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.IsEmpty) + { + this.mAllowCertificateStatus = false; + } + + if (this.mAllowCertificateStatus) + { + CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus(); + if (certificateStatus != null) + { + SendCertificateStatusMessage(certificateStatus); + } + } + + this.mConnectionState = CS_CERTIFICATE_STATUS; + + byte[] serverKeyExchange = this.mKeyExchange.GenerateServerKeyExchange(); + if (serverKeyExchange != null) + { + SendServerKeyExchangeMessage(serverKeyExchange); + } + this.mConnectionState = CS_SERVER_KEY_EXCHANGE; + + if (this.mServerCredentials != null) + { + this.mCertificateRequest = mTlsServer.GetCertificateRequest(); + if (this.mCertificateRequest != null) + { + this.mKeyExchange.ValidateCertificateRequest(mCertificateRequest); + + SendCertificateRequestMessage(mCertificateRequest); + + TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash, + this.mCertificateRequest.SupportedSignatureAlgorithms); + } + } + this.mConnectionState = CS_CERTIFICATE_REQUEST; + + SendServerHelloDoneMessage(); + this.mConnectionState = CS_SERVER_HELLO_DONE; + + this.mRecordStream.HandshakeHash.SealHashAlgorithms(); + + break; + } + case CS_END: + { + RefuseRenegotiation(); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.supplemental_data: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO_DONE: + { + mTlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf)); + this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.certificate: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO_DONE: + case CS_CLIENT_SUPPLEMENTAL_DATA: + { + if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA) + { + mTlsServer.ProcessClientSupplementalData(null); + } + + if (this.mCertificateRequest == null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ReceiveCertificateMessage(buf); + this.mConnectionState = CS_CLIENT_CERTIFICATE; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.client_key_exchange: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO_DONE: + case CS_CLIENT_SUPPLEMENTAL_DATA: + case CS_CLIENT_CERTIFICATE: + { + if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA) + { + mTlsServer.ProcessClientSupplementalData(null); + } + + if (mConnectionState < CS_CLIENT_CERTIFICATE) + { + if (this.mCertificateRequest == null) + { + this.mKeyExchange.SkipClientCredentials(); + } + else + { + if (TlsUtilities.IsTlsV12(Context)) + { + /* + * RFC 5246 If no suitable certificate is available, the client MUST Send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + else if (TlsUtilities.IsSsl(Context)) + { + if (this.mPeerCertificate == null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + else + { + NotifyClientCertificate(Certificate.EmptyChain); + } + } + } + + ReceiveClientKeyExchangeMessage(buf); + this.mConnectionState = CS_CLIENT_KEY_EXCHANGE; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.certificate_verify: + { + switch (this.mConnectionState) + { + case CS_CLIENT_KEY_EXCHANGE: + { + /* + * RFC 5246 7.4.8 This message is only sent following a client certificate that has + * signing capability (i.e., all certificates except those containing fixed + * Diffie-Hellman parameters). + */ + if (!ExpectCertificateVerifyMessage()) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ReceiveCertificateVerifyMessage(buf); + this.mConnectionState = CS_CERTIFICATE_VERIFY; + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.finished: + { + switch (this.mConnectionState) + { + case CS_CLIENT_KEY_EXCHANGE: + case CS_CERTIFICATE_VERIFY: + { + if (mConnectionState < CS_CERTIFICATE_VERIFY && ExpectCertificateVerifyMessage()) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ProcessFinishedMessage(buf); + this.mConnectionState = CS_CLIENT_FINISHED; + + if (this.mExpectSessionTicket) + { + SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket()); + SendChangeCipherSpecMessage(); + } + this.mConnectionState = CS_SERVER_SESSION_TICKET; + + SendFinishedMessage(); + this.mConnectionState = CS_SERVER_FINISHED; + this.mConnectionState = CS_END; + + CompleteHandshake(); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.hello_request: + case HandshakeType.hello_verify_request: + case HandshakeType.server_hello: + case HandshakeType.server_key_exchange: + case HandshakeType.certificate_request: + case HandshakeType.server_hello_done: + case HandshakeType.session_ticket: + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + protected override void HandleWarningMessage(byte description) + { + switch (description) + { + case AlertDescription.no_certificate: + { + /* + * SSL 3.0 If the server has sent a certificate request Message, the client must Send + * either the certificate message or a no_certificate alert. + */ + if (TlsUtilities.IsSsl(Context) && mCertificateRequest != null) + { + NotifyClientCertificate(Certificate.EmptyChain); + } + break; + } + default: + { + base.HandleWarningMessage(description); + break; + } + } + } + + protected virtual void NotifyClientCertificate(Certificate clientCertificate) + { + if (mCertificateRequest == null) + throw new InvalidOperationException(); + if (mPeerCertificate != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + this.mPeerCertificate = clientCertificate; + + if (clientCertificate.IsEmpty) + { + this.mKeyExchange.SkipClientCredentials(); + } + else + { + + /* + * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request + * message was non-empty, one of the certificates in the certificate chain SHOULD be + * issued by one of the listed CAs. + */ + + this.mClientCertificateType = TlsUtilities.GetClientCertificateType(clientCertificate, + this.mServerCredentials.Certificate); + + this.mKeyExchange.ProcessClientCertificate(clientCertificate); + } + + /* + * RFC 5246 7.4.6. If the client does not Send any certificates, the server MAY at its + * discretion either continue the handshake without client authentication, or respond with a + * fatal handshake_failure alert. Also, if some aspect of the certificate chain was + * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its + * discretion either continue the handshake (considering the client unauthenticated) or Send + * a fatal alert. + */ + this.mTlsServer.NotifyClientCertificate(clientCertificate); + } + + protected virtual void ReceiveCertificateMessage(MemoryStream buf) + { + Certificate clientCertificate = Certificate.Parse(buf); + + AssertEmpty(buf); + + NotifyClientCertificate(clientCertificate); + } + + protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf) + { + DigitallySigned clientCertificateVerify = DigitallySigned.Parse(Context, buf); + + AssertEmpty(buf); + + // Verify the CertificateVerify message contains a correct signature. + try + { + byte[] hash; + if (TlsUtilities.IsTlsV12(Context)) + { + hash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + } + else + { + hash = mSecurityParameters.SessionHash; + } + + X509CertificateStructure x509Cert = mPeerCertificate.GetCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo); + + TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType); + tlsSigner.Init(Context); + if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, + clientCertificateVerify.Signature, publicKey, hash)) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error, e); + } + } + + protected virtual void ReceiveClientHelloMessage(MemoryStream buf) + { + ProtocolVersion client_version = TlsUtilities.ReadVersion(buf); + mRecordStream.SetWriteVersion(client_version); + + if (client_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + byte[] client_random = TlsUtilities.ReadFully(32, buf); + + /* + * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to + * use the Session ID in the ClientHello for stateful session resumption. + */ + byte[] sessionID = TlsUtilities.ReadOpaque8(buf); + if (sessionID.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + /* + * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session + * resumption request), this vector MUST include at least the cipher_suite from that + * session. + */ + int cipher_suites_length = TlsUtilities.ReadUint16(buf); + if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + this.mOfferedCipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, buf); + + /* + * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session + * resumption request), it MUST include the compression_method from that session. + */ + int compression_methods_length = TlsUtilities.ReadUint8(buf); + if (compression_methods_length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mOfferedCompressionMethods = TlsUtilities.ReadUint8Array(compression_methods_length, buf); + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and Send a server hello containing no + * extensions. + */ + this.mClientExtensions = ReadExtensions(buf); + + /* + * TODO[session-hash] + * + * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes + * that do not use the extended master secret [..]. (and see 5.2, 5.3) + */ + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); + + ContextAdmin.SetClientVersion(client_version); + + mTlsServer.NotifyClientVersion(client_version); + mTlsServer.NotifyFallback(Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)); + + mSecurityParameters.clientRandom = client_random; + + mTlsServer.NotifyOfferedCipherSuites(mOfferedCipherSuites); + mTlsServer.NotifyOfferedCompressionMethods(mOfferedCompressionMethods); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + + /* + * When a ClientHello is received, the server MUST check if it includes the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag + * to TRUE. + */ + if (Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + { + this.mSecureRenegotiation = true; + } + + /* + * The server MUST check if the "renegotiation_info" extension is included in the + * ClientHello. + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info); + if (renegExtData != null) + { + /* + * If the extension is present, set secure_renegotiation flag to TRUE. The + * server MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake. + */ + this.mSecureRenegotiation = true; + + if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + mTlsServer.NotifySecureRenegotiation(this.mSecureRenegotiation); + + if (mClientExtensions != null) + { + mTlsServer.ProcessClientExtensions(mClientExtensions); + } + } + + protected virtual void ReceiveClientKeyExchangeMessage(MemoryStream buf) + { + mKeyExchange.ProcessClientKeyExchange(buf); + + AssertEmpty(buf); + + this.mPrepareFinishHash = mRecordStream.PrepareToFinish(); + this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, mPrepareFinishHash, null); + + EstablishMasterSecret(Context, mKeyExchange); + mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); + + if (!mExpectSessionTicket) + { + SendChangeCipherSpecMessage(); + } + } + + protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request); + + certificateRequest.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendCertificateStatusMessage(CertificateStatus certificateStatus) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status); + + certificateStatus.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendNewSessionTicketMessage(NewSessionTicket newSessionTicket) + { + if (newSessionTicket == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket); + + newSessionTicket.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendServerHelloMessage() + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello); + + { + ProtocolVersion server_version = mTlsServer.GetServerVersion(); + if (!server_version.IsEqualOrEarlierVersionOf(Context.ClientVersion)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + mRecordStream.ReadVersion = server_version; + mRecordStream.SetWriteVersion(server_version); + mRecordStream.SetRestrictReadVersion(true); + ContextAdmin.SetServerVersion(server_version); + + TlsUtilities.WriteVersion(server_version, message); + } + + message.Write(this.mSecurityParameters.serverRandom); + + /* + * The server may return an empty session_id to indicate that the session will not be cached + * and therefore cannot be resumed. + */ + TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, message); + + int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite(); + if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || CipherSuite.IsScsv(selectedCipherSuite) + || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + mSecurityParameters.cipherSuite = selectedCipherSuite; + + byte selectedCompressionMethod = mTlsServer.GetSelectedCompressionMethod(); + if (!Arrays.Contains(mOfferedCompressionMethods, selectedCompressionMethod)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; + + TlsUtilities.WriteUint16(selectedCipherSuite, message); + TlsUtilities.WriteUint8(selectedCompressionMethod, message); + + this.mServerExtensions = mTlsServer.GetServerExtensions(); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + if (this.mSecureRenegotiation) + { + byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); + bool noRenegExt = (null == renegExtData); + + if (noRenegExt) + { + /* + * Note that Sending a "renegotiation_info" extension in response to a ClientHello + * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, + * Section 7.4.1.4, on the server Sending unsolicited extensions and is only allowed + * because the client is signaling its willingness to receive the extension via the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + + /* + * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty + * "renegotiation_info" extension in the ServerHello message. + */ + this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions); + this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); + } + } + + if (mSecurityParameters.extendedMasterSecret) + { + this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions); + TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions); + } + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and Send a server hello containing no + * extensions. + */ + + if (this.mServerExtensions != null) + { + this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); + + this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(mClientExtensions, + mServerExtensions, AlertDescription.internal_error); + + this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + this.mAllowCertificateStatus = !mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.status_request, + AlertDescription.internal_error); + + this.mExpectSessionTicket = !mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.session_ticket, + AlertDescription.internal_error); + + WriteExtensions(message, this.mServerExtensions); + } + + mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has + * a verify_data_length equal to 12. This includes all existing cipher suites. + */ + mSecurityParameters.verifyDataLength = 12; + + ApplyMaxFragmentLengthExtension(); + + message.WriteToRecordStream(this); + } + + protected virtual void SendServerHelloDoneMessage() + { + byte[] message = new byte[4]; + TlsUtilities.WriteUint8(HandshakeType.server_hello_done, message, 0); + TlsUtilities.WriteUint24(0, message, 1); + + WriteHandshakeMessage(message, 0, message.Length); + } + + protected virtual void SendServerKeyExchangeMessage(byte[] serverKeyExchange) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.Length); + + message.Write(serverKeyExchange); + + message.WriteToRecordStream(this); + } + + protected virtual bool ExpectCertificateVerifyMessage() + { + return mClientCertificateType >= 0 && TlsUtilities.HasSigningCapability((byte)mClientCertificateType); + } + } +} diff --git a/crypto/src/crypto/tls/TlsSession.cs b/crypto/src/crypto/tls/TlsSession.cs new file mode 100644
index 000000000..6c229913b --- /dev/null +++ b/crypto/src/crypto/tls/TlsSession.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsSession + { + SessionParameters ExportSessionParameters(); + + byte[] SessionID { get; } + + void Invalidate(); + + bool IsResumable { get; } + } +} diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs new file mode 100644
index 000000000..866392623 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSessionImpl.cs
@@ -0,0 +1,54 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsSessionImpl + : TlsSession + { + internal readonly byte[] mSessionID; + internal SessionParameters mSessionParameters; + + internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters) + { + if (sessionID == null) + throw new ArgumentNullException("sessionID"); + if (sessionID.Length < 1 || sessionID.Length > 32) + throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID"); + + this.mSessionID = Arrays.Clone(sessionID); + this.mSessionParameters = sessionParameters; + } + + public virtual SessionParameters ExportSessionParameters() + { + lock (this) + { + return this.mSessionParameters == null ? null : this.mSessionParameters.Copy(); + } + } + + public virtual byte[] SessionID + { + get { lock (this) return mSessionID; } + } + + public virtual void Invalidate() + { + lock (this) + { + if (this.mSessionParameters != null) + { + this.mSessionParameters.Clear(); + this.mSessionParameters = null; + } + } + } + + public virtual bool IsResumable + { + get { lock (this) return this.mSessionParameters != null; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsSigner.cs b/crypto/src/crypto/tls/TlsSigner.cs
index 79d468fee..ffdd4c9a1 100644 --- a/crypto/src/crypto/tls/TlsSigner.cs +++ b/crypto/src/crypto/tls/TlsSigner.cs
@@ -1,18 +1,29 @@ using System; -using Org.BouncyCastle.Security; - namespace Org.BouncyCastle.Crypto.Tls { public interface TlsSigner { - byte[] GenerateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey, - byte[] md5andsha1); - bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1); + void Init(TlsContext context); + + byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1); + + byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash); + + bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1); + + bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash); + + ISigner CreateSigner(AsymmetricKeyParameter privateKey); + + ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); - ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey); ISigner CreateVerifyer(AsymmetricKeyParameter publicKey); + ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); + bool IsValidPublicKey(AsymmetricKeyParameter publicKey); } } diff --git a/crypto/src/crypto/tls/TlsSignerCredentials.cs b/crypto/src/crypto/tls/TlsSignerCredentials.cs
index 2adb06c26..92ed7cc19 100644 --- a/crypto/src/crypto/tls/TlsSignerCredentials.cs +++ b/crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -3,9 +3,12 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsSignerCredentials : TlsCredentials - { - /// <exception cref="IOException"></exception> - byte[] GenerateCertificateSignature(byte[] md5andsha1); - } + public interface TlsSignerCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateCertificateSignature(byte[] hash); + + SignatureAndHashAlgorithm SignatureAndHashAlgorithm { get; } + } } diff --git a/crypto/src/crypto/tls/TlsSrpGroupVerifier.cs b/crypto/src/crypto/tls/TlsSrpGroupVerifier.cs new file mode 100644
index 000000000..185f2f50a --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrpGroupVerifier.cs
@@ -0,0 +1,17 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsSrpGroupVerifier + { + /** + * Check whether the given SRP group parameters are acceptable for use. + * + * @param group the {@link SRP6GroupParameters} to check + * @return true if (and only if) the specified group parameters are acceptable + */ + bool Accept(Srp6GroupParameters group); + } +} diff --git a/crypto/src/crypto/tls/TlsSrpIdentityManager.cs b/crypto/src/crypto/tls/TlsSrpIdentityManager.cs new file mode 100644
index 000000000..080a0dc16 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrpIdentityManager.cs
@@ -0,0 +1,21 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsSrpIdentityManager + { + /** + * Lookup the {@link TlsSRPLoginParameters} corresponding to the specified identity. + * + * NOTE: To avoid "identity probing", unknown identities SHOULD be handled as recommended in RFC + * 5054 2.5.1.3. {@link SimulatedTlsSRPIdentityManager} is provided for this purpose. + * + * @param identity + * the SRP identity sent by the connecting client + * @return the {@link TlsSRPLoginParameters} for the specified identity, or else 'simulated' + * parameters if the identity is not recognized. A null value is also allowed, but not + * recommended. + */ + TlsSrpLoginParameters GetLoginParameters(byte[] identity); + } +} diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
index 950be87ba..ce8e4834a 100644 --- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -1,200 +1,282 @@ 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.Agreement; using Org.BouncyCastle.Crypto.Agreement.Srp; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.IO; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// TLS 1.1 SRP key exchange. - /// </summary> - internal class TlsSrpKeyExchange - : TlsKeyExchange + /// <summary>(D)TLS SRP key exchange (RFC 5054).</summary> + public class TlsSrpKeyExchange + : AbstractTlsKeyExchange { - protected TlsClientContext context; - protected int keyExchange; - protected TlsSigner tlsSigner; - protected byte[] identity; - protected byte[] password; - - protected AsymmetricKeyParameter serverPublicKey = null; - - protected byte[] s = null; - protected BigInteger B = null; - protected Srp6Client srpClient = new Srp6Client(); - - internal TlsSrpKeyExchange(TlsClientContext context, int keyExchange, - byte[] identity, byte[] password) + protected static TlsSigner CreateSigner(int keyExchange) { switch (keyExchange) { case KeyExchangeAlgorithm.SRP: - this.tlsSigner = null; - break; + return null; case KeyExchangeAlgorithm.SRP_RSA: - this.tlsSigner = new TlsRsaSigner(); - break; + return new TlsRsaSigner(); case KeyExchangeAlgorithm.SRP_DSS: - this.tlsSigner = new TlsDssSigner(); - break; + return new TlsDssSigner(); default: - throw new ArgumentException("unsupported key exchange algorithm", "keyExchange"); + throw new ArgumentException("unsupported key exchange algorithm"); } + } + + protected TlsSigner mTlsSigner; + protected TlsSrpGroupVerifier mGroupVerifier; + protected byte[] mIdentity; + protected byte[] mPassword; + + protected AsymmetricKeyParameter mServerPublicKey = null; + + protected Srp6GroupParameters mSrpGroup = null; + protected Srp6Client mSrpClient = null; + protected Srp6Server mSrpServer = null; + protected BigInteger mSrpPeerCredentials = null; + protected BigInteger mSrpVerifier = null; + protected byte[] mSrpSalt = null; + + protected TlsSignerCredentials mServerCredentials = null; + + [Obsolete("Use constructor taking an explicit 'groupVerifier' argument")] + public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity, byte[] password) + : this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsSrpGroupVerifier(), identity, password) + { + } + + public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsSrpGroupVerifier groupVerifier, + byte[] identity, byte[] password) + : base(keyExchange, supportedSignatureAlgorithms) + { + this.mTlsSigner = CreateSigner(keyExchange); + this.mGroupVerifier = groupVerifier; + this.mIdentity = identity; + this.mPassword = password; + this.mSrpClient = new Srp6Client(); + } - this.context = context; - this.keyExchange = keyExchange; - this.identity = identity; - this.password = password; + public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity, + TlsSrpLoginParameters loginParameters) + : base(keyExchange, supportedSignatureAlgorithms) + { + this.mTlsSigner = CreateSigner(keyExchange); + this.mIdentity = identity; + this.mSrpServer = new Srp6Server(); + this.mSrpGroup = loginParameters.Group; + this.mSrpVerifier = loginParameters.Verifier; + this.mSrpSalt = loginParameters.Salt; } - public virtual void SkipServerCertificate() + public override void Init(TlsContext context) { - if (tlsSigner != null) + base.Init(context); + + if (this.mTlsSigner != null) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + this.mTlsSigner.Init(context); } } - public virtual void ProcessServerCertificate(Certificate serverCertificate) + public override void SkipServerCredentials() { - if (tlsSigner == null) - { + if (mTlsSigner != null) throw new TlsFatalAlert(AlertDescription.unexpected_message); - } + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (mTlsSigner == null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); - SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; try { - this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); } -// catch (RuntimeException) - catch (Exception) + catch (Exception e) { - throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); } - if (!tlsSigner.IsValidPublicKey(this.serverPublicKey)) - { + if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey)) throw new TlsFatalAlert(AlertDescription.certificate_unknown); - } TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); - // TODO - /* - * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the - * signing algorithm for the certificate must be the same as the algorithm for the - * certificate key." - */ + base.ProcessServerCertificate(serverCertificate); } - public virtual void SkipServerKeyExchange() + public override void ProcessServerCredentials(TlsCredentials serverCredentials) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + if ((mKeyExchange == KeyExchangeAlgorithm.SRP) || !(serverCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsSignerCredentials)serverCredentials; + } + + public override bool RequiresServerKeyExchange + { + get { return true; } } - public virtual void ProcessServerKeyExchange(Stream input) + public override byte[] GenerateServerKeyExchange() { - SecurityParameters securityParameters = context.SecurityParameters; + mSrpServer.Init(mSrpGroup, mSrpVerifier, TlsUtilities.CreateHash(HashAlgorithm.sha1), mContext.SecureRandom); + BigInteger B = mSrpServer.GenerateServerCredentials(); - Stream sigIn = input; - ISigner signer = null; + ServerSrpParams srpParams = new ServerSrpParams(mSrpGroup.N, mSrpGroup.G, mSrpSalt, B); - if (tlsSigner != null) + DigestInputBuffer buf = new DigestInputBuffer(); + + srpParams.Encode(buf); + + if (mServerCredentials != null) { - signer = InitSigner(tlsSigner, securityParameters); - sigIn = new SignerStream(input, signer, null); + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm( + mContext, mServerCredentials); + + IDigest d = TlsUtilities.CreateHash(signatureAndHashAlgorithm); + + SecurityParameters securityParameters = mContext.SecurityParameters; + d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + buf.UpdateDigest(d); + + byte[] hash = new byte[d.GetDigestSize()]; + d.DoFinal(hash, 0); + + byte[] signature = mServerCredentials.GenerateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.Encode(buf); } - byte[] NBytes = TlsUtilities.ReadOpaque16(sigIn); - byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn); - byte[] sBytes = TlsUtilities.ReadOpaque8(sigIn); - byte[] BBytes = TlsUtilities.ReadOpaque16(sigIn); + return buf.ToArray(); + } + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = mContext.SecurityParameters; + + SignerInputBuffer buf = null; + Stream teeIn = input; - if (signer != null) + if (mTlsSigner != null) { - byte[] sigByte = TlsUtilities.ReadOpaque16(input); + buf = new SignerInputBuffer(); + teeIn = new TeeInputStream(input, buf); + } - if (!signer.VerifySignature(sigByte)) - { + ServerSrpParams srpParams = ServerSrpParams.Parse(teeIn); + + if (buf != null) + { + DigitallySigned signed_params = DigitallySigned.Parse(mContext, input); + + ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters); + buf.UpdateSigner(signer); + if (!signer.VerifySignature(signed_params.Signature)) throw new TlsFatalAlert(AlertDescription.decrypt_error); - } } - BigInteger N = new BigInteger(1, NBytes); - BigInteger g = new BigInteger(1, gBytes); + this.mSrpGroup = new Srp6GroupParameters(srpParams.N, srpParams.G); - // TODO Validate group parameters (see RFC 5054) - //throw new TlsFatalAlert(AlertDescription.insufficient_security); + if (!mGroupVerifier.Accept(mSrpGroup)) + throw new TlsFatalAlert(AlertDescription.insufficient_security); - this.s = sBytes; + this.mSrpSalt = srpParams.S; /* - * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" - * alert if B % N = 0. - */ + * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if + * B % N = 0. + */ try { - this.B = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes)); + this.mSrpPeerCredentials = Srp6Utilities.ValidatePublicValue(mSrpGroup.N, srpParams.B); } - catch (CryptoException) + catch (CryptoException e) { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); } - this.srpClient.Init(N, g, new Sha1Digest(), context.SecureRandom); + this.mSrpClient.Init(mSrpGroup, TlsUtilities.CreateHash(HashAlgorithm.sha1), mContext.SecureRandom); } - public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public virtual void SkipClientCredentials() + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { - // OK + throw new TlsFatalAlert(AlertDescription.internal_error); } - - public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + + public override void GenerateClientKeyExchange(Stream output) { - throw new TlsFatalAlert(AlertDescription.internal_error); + BigInteger A = mSrpClient.GenerateClientCredentials(mSrpSalt, mIdentity, mPassword); + TlsSrpUtilities.WriteSrpParameter(A, output); + + mContext.SecurityParameters.srpIdentity = Arrays.Clone(mIdentity); } - public virtual void GenerateClientKeyExchange(Stream output) + public override void ProcessClientKeyExchange(Stream input) { - byte[] keData = BigIntegers.AsUnsignedByteArray(srpClient.GenerateClientCredentials(s, - this.identity, this.password)); - TlsUtilities.WriteOpaque16(keData, output); + /* + * RFC 5054 2.5.4: The server MUST abort the handshake with an "illegal_parameter" alert if + * A % N = 0. + */ + try + { + this.mSrpPeerCredentials = Srp6Utilities.ValidatePublicValue(mSrpGroup.N, TlsSrpUtilities.ReadSrpParameter(input)); + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + + mContext.SecurityParameters.srpIdentity = Arrays.Clone(mIdentity); } - public virtual byte[] GeneratePremasterSecret() + public override byte[] GeneratePremasterSecret() { try { + BigInteger S = mSrpServer != null + ? mSrpServer.CalculateSecret(mSrpPeerCredentials) + : mSrpClient.CalculateSecret(mSrpPeerCredentials); + // TODO Check if this needs to be a fixed size - return BigIntegers.AsUnsignedByteArray(srpClient.CalculateSecret(B)); + return BigIntegers.AsUnsignedByteArray(S); } - catch (CryptoException) + catch (CryptoException e) { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); } } - protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters) + protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, + SecurityParameters securityParameters) { - ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey); signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); return signer; diff --git a/crypto/src/crypto/tls/TlsSrpLoginParameters.cs b/crypto/src/crypto/tls/TlsSrpLoginParameters.cs new file mode 100644
index 000000000..5ae4641f6 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrpLoginParameters.cs
@@ -0,0 +1,36 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsSrpLoginParameters + { + protected readonly Srp6GroupParameters mGroup; + protected readonly BigInteger mVerifier; + protected readonly byte[] mSalt; + + public TlsSrpLoginParameters(Srp6GroupParameters group, BigInteger verifier, byte[] salt) + { + this.mGroup = group; + this.mVerifier = verifier; + this.mSalt = salt; + } + + public virtual Srp6GroupParameters Group + { + get { return mGroup; } + } + + public virtual byte[] Salt + { + get { return mSalt; } + } + + public virtual BigInteger Verifier + { + get { return mVerifier; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs new file mode 100644
index 000000000..873189dc6 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs
@@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsSrpUtilities + { + public static void AddSrpExtension(IDictionary extensions, byte[] identity) + { + extensions[ExtensionType.srp] = CreateSrpExtension(identity); + } + + public static byte[] GetSrpExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.srp); + return extensionData == null ? null : ReadSrpExtension(extensionData); + } + + public static byte[] CreateSrpExtension(byte[] identity) + { + if (identity == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeOpaque8(identity); + } + + public static byte[] ReadSrpExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + byte[] identity = TlsUtilities.ReadOpaque8(buf); + + TlsProtocol.AssertEmpty(buf); + + return identity; + } + + public static BigInteger ReadSrpParameter(Stream input) + { + return new BigInteger(1, TlsUtilities.ReadOpaque16(input)); + } + + public static void WriteSrpParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output); + } + + public static bool IsSrpCipherSuite(int cipherSuite) + { + switch (cipherSuite) + { + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return true; + + default: + return false; + } + } + } +} diff --git a/crypto/src/crypto/tls/TlsSrtpUtilities.cs b/crypto/src/crypto/tls/TlsSrtpUtilities.cs new file mode 100644
index 000000000..626c0e3a4 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrtpUtilities.cs
@@ -0,0 +1,62 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5764 DTLS Extension to Establish Keys for SRTP. + */ + public abstract class TlsSRTPUtils + { + public static void AddUseSrtpExtension(IDictionary extensions, UseSrtpData useSRTPData) + { + extensions[ExtensionType.use_srtp] = CreateUseSrtpExtension(useSRTPData); + } + + public static UseSrtpData GetUseSrtpExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.use_srtp); + return extensionData == null ? null : ReadUseSrtpExtension(extensionData); + } + + public static byte[] CreateUseSrtpExtension(UseSrtpData useSrtpData) + { + if (useSrtpData == null) + throw new ArgumentNullException("useSrtpData"); + + MemoryStream buf = new MemoryStream(); + + // SRTPProtectionProfiles + TlsUtilities.WriteUint16ArrayWithUint16Length(useSrtpData.ProtectionProfiles, buf); + + // srtp_mki + TlsUtilities.WriteOpaque8(useSrtpData.Mki, buf); + + return buf.ToArray(); + } + + public static UseSrtpData ReadUseSrtpExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, true); + + // SRTPProtectionProfiles + int length = TlsUtilities.ReadUint16(buf); + if (length < 2 || (length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + int[] protectionProfiles = TlsUtilities.ReadUint16Array(length / 2, buf); + + // srtp_mki + byte[] mki = TlsUtilities.ReadOpaque8(buf); + + TlsProtocol.AssertEmpty(buf); + + return new UseSrtpData(protectionProfiles, mki); + } + } +} diff --git a/crypto/src/crypto/tls/TlsStream.cs b/crypto/src/crypto/tls/TlsStream.cs
index fe24ad3ed..7ff7184e3 100644 --- a/crypto/src/crypto/tls/TlsStream.cs +++ b/crypto/src/crypto/tls/TlsStream.cs
@@ -3,84 +3,83 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsStream - : Stream - { - private readonly TlsProtocolHandler handler; + internal class TlsStream + : Stream + { + private readonly TlsProtocol handler; - internal TlsStream( - TlsProtocolHandler handler) - { - this.handler = handler; - } + internal TlsStream(TlsProtocol handler) + { + this.handler = handler; + } - public override bool CanRead - { - get { return !handler.IsClosed; } - } + public override bool CanRead + { + get { return !handler.IsClosed; } + } - public override bool CanSeek - { - get { return false; } - } + public override bool CanSeek + { + get { return false; } + } - public override bool CanWrite - { - get { return !handler.IsClosed; } - } + public override bool CanWrite + { + get { return !handler.IsClosed; } + } - public override void Close() - { - handler.Close(); - } + public override void Close() + { + handler.Close(); + } - public override void Flush() - { - handler.Flush(); - } + public override void Flush() + { + handler.Flush(); + } public override long Length - { - get { throw new NotSupportedException(); } - } + { + get { throw new NotSupportedException(); } + } - public override long Position + public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } - public override int Read(byte[] buf, int off, int len) - { - return this.handler.ReadApplicationData(buf, off, len); - } + public override int Read(byte[] buf, int off, int len) + { + return this.handler.ReadApplicationData(buf, off, len); + } - public override int ReadByte() - { - byte[] buf = new byte[1]; - if (this.Read(buf, 0, 1) <= 0) - return -1; - return buf[0]; - } + public override int ReadByte() + { + byte[] buf = new byte[1]; + if (this.Read(buf, 0, 1) <= 0) + return -1; + return buf[0]; + } - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } public override void SetLength(long value) - { - throw new NotSupportedException(); - } + { + throw new NotSupportedException(); + } - public override void Write(byte[] buf, int off, int len) - { - this.handler.WriteData(buf, off, len); - } + public override void Write(byte[] buf, int off, int len) + { + this.handler.WriteData(buf, off, len); + } - public override void WriteByte(byte b) - { - this.handler.WriteData(new byte[] { b }, 0, 1); - } - } + public override void WriteByte(byte b) + { + this.handler.WriteData(new byte[] { b }, 0, 1); + } + } } diff --git a/crypto/src/crypto/tls/TlsStreamCipher.cs b/crypto/src/crypto/tls/TlsStreamCipher.cs
index 35f794d96..45f2a9a70 100644 --- a/crypto/src/crypto/tls/TlsStreamCipher.cs +++ b/crypto/src/crypto/tls/TlsStreamCipher.cs
@@ -1,108 +1,164 @@ using System; +using System.IO; -using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Tls; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public class TlsStreamCipher : TlsCipher + public class TlsStreamCipher + : TlsCipher { - protected TlsClientContext context; + protected readonly TlsContext context; - protected IStreamCipher encryptCipher; - protected IStreamCipher decryptCipher; + protected readonly IStreamCipher encryptCipher; + protected readonly IStreamCipher decryptCipher; - protected TlsMac writeMac; - protected TlsMac readMac; + protected readonly TlsMac writeMac; + protected readonly TlsMac readMac; - public TlsStreamCipher(TlsClientContext context, IStreamCipher encryptCipher, - IStreamCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize) + protected readonly bool usesNonce; + + /// <exception cref="IOException"></exception> + public TlsStreamCipher(TlsContext context, IStreamCipher clientWriteCipher, + IStreamCipher serverWriteCipher, IDigest clientWriteDigest, IDigest serverWriteDigest, + int cipherKeySize, bool usesNonce) { + bool isServer = context.IsServer; + this.context = context; - this.encryptCipher = encryptCipher; - this.decryptCipher = decryptCipher; + this.usesNonce = usesNonce; - int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize() - + readDigest.GetDigestSize(); + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; - SecurityParameters securityParameters = context.SecurityParameters; + int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize() + + serverWriteDigest.GetDigestSize(); - byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion", - TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom), - prfSize); + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); int offset = 0; // Init MACs - writeMac = CreateTlsMac(writeDigest, keyBlock, ref offset); - readMac = CreateTlsMac(readDigest, keyBlock, ref offset); + TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.GetDigestSize()); + offset += clientWriteDigest.GetDigestSize(); + TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.GetDigestSize()); + offset += serverWriteDigest.GetDigestSize(); // Build keys - KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize); - KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize); + KeyParameter clientWriteKey = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter serverWriteKey = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; - if (offset != prfSize) + if (offset != key_block_size) + { throw new TlsFatalAlert(AlertDescription.internal_error); + } - // Init Ciphers - encryptCipher.Init(true, encryptKey); - decryptCipher.Init(false, decryptKey); - } - - public byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len) - { - byte[] mac = writeMac.CalculateMac(type, plaintext, offset, len); - int size = len + mac.Length; + ICipherParameters encryptParams, decryptParams; + if (isServer) + { + this.writeMac = serverWriteMac; + this.readMac = clientWriteMac; + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + encryptParams = serverWriteKey; + decryptParams = clientWriteKey; + } + else + { + this.writeMac = clientWriteMac; + this.readMac = serverWriteMac; + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + encryptParams = clientWriteKey; + decryptParams = serverWriteKey; + } - byte[] outbuf = new byte[size]; + if (usesNonce) + { + byte[] dummyNonce = new byte[8]; + encryptParams = new ParametersWithIV(encryptParams, dummyNonce); + decryptParams = new ParametersWithIV(decryptParams, dummyNonce); + } - encryptCipher.ProcessBytes(plaintext, offset, len, outbuf, 0); - encryptCipher.ProcessBytes(mac, 0, mac.Length, outbuf, len); + this.encryptCipher.Init(true, encryptParams); + this.decryptCipher.Init(false, decryptParams); + } - return outbuf; + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + return ciphertextLimit - writeMac.Size; } - public byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len) + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) { - byte[] deciphered = new byte[len]; - decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0); + /* + * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That + * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS + * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation + * of the 16-bit epoch with the 48-bit sequence number. + */ + if (usesNonce) + { + UpdateIV(encryptCipher, true, seqNo); + } - int plaintextSize = deciphered.Length - readMac.Size; - byte[] plainText = CopyData(deciphered, 0, plaintextSize); + byte[] outBuf = new byte[len + writeMac.Size]; - byte[] receivedMac = CopyData(deciphered, plaintextSize, readMac.Size); - byte[] computedMac = readMac.CalculateMac(type, plainText, 0, plainText.Length); + encryptCipher.ProcessBytes(plaintext, offset, len, outBuf, 0); - if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac)) - { - throw new TlsFatalAlert(AlertDescription.bad_record_mac); - } + byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len); + encryptCipher.ProcessBytes(mac, 0, mac.Length, outBuf, len); - return plainText; + return outBuf; } - protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off) + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) { - int len = digest.GetDigestSize(); - TlsMac mac = new TlsMac(digest, buf, off, len); - off += len; - return mac; + /* + * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That + * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS + * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation + * of the 16-bit epoch with the 48-bit sequence number. + */ + if (usesNonce) + { + UpdateIV(decryptCipher, false, seqNo); + } + + int macSize = readMac.Size; + if (len < macSize) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int plaintextLength = len - macSize; + + byte[] deciphered = new byte[len]; + decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0); + CheckMac(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength); + return Arrays.CopyOfRange(deciphered, 0, plaintextLength); } - protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len) + /// <exception cref="IOException"></exception> + protected virtual void CheckMac(long seqNo, byte type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen) { - KeyParameter key = new KeyParameter(buf, off, len); - off += len; - return key; + byte[] receivedMac = Arrays.CopyOfRange(recBuf, recStart, recEnd); + byte[] computedMac = readMac.CalculateMac(seqNo, type, calcBuf, calcOff, calcLen); + + if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac)) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); } - protected virtual byte[] CopyData(byte[] text, int offset, int len) + protected virtual void UpdateIV(IStreamCipher cipher, bool forEncryption, long seqNo) { - byte[] result = new byte[len]; - Array.Copy(text, offset, result, 0, len); - return result; + byte[] nonce = new byte[8]; + TlsUtilities.WriteUint64(seqNo, nonce, 0); + cipher.Init(forEncryption, new ParametersWithIV(null, nonce)); } } } diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 2309fc3da..a8c8a2b28 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -4,10 +4,13 @@ using System.IO; using System.Text; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Date; using Org.BouncyCastle.Utilities.IO; @@ -15,9 +18,12 @@ using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { /// <remarks>Some helper functions for MicroTLS.</remarks> - public class TlsUtilities + public abstract class TlsUtilities { public static readonly byte[] EmptyBytes = new byte[0]; + public static readonly short[] EmptyShorts = new short[0]; + public static readonly int[] EmptyInts = new int[0]; + public static readonly long[] EmptyLongs = new long[0]; public static void CheckUint8(int i) { @@ -25,82 +31,199 @@ namespace Org.BouncyCastle.Crypto.Tls throw new TlsFatalAlert(AlertDescription.internal_error); } + public static void CheckUint8(long i) + { + if (!IsValidUint8(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + public static void CheckUint16(int i) { if (!IsValidUint16(i)) throw new TlsFatalAlert(AlertDescription.internal_error); } + public static void CheckUint16(long i) + { + if (!IsValidUint16(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + public static void CheckUint24(int i) { if (!IsValidUint24(i)) throw new TlsFatalAlert(AlertDescription.internal_error); } + public static void CheckUint24(long i) + { + if (!IsValidUint24(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint32(long i) + { + if (!IsValidUint32(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint48(long i) + { + if (!IsValidUint48(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint64(long i) + { + if (!IsValidUint64(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + public static bool IsValidUint8(int i) { return (i & 0xFF) == i; } + public static bool IsValidUint8(long i) + { + return (i & 0xFFL) == i; + } + public static bool IsValidUint16(int i) { return (i & 0xFFFF) == i; } + public static bool IsValidUint16(long i) + { + return (i & 0xFFFFL) == i; + } + public static bool IsValidUint24(int i) { return (i & 0xFFFFFF) == i; } - internal static void WriteUint8(byte i, Stream os) + public static bool IsValidUint24(long i) + { + return (i & 0xFFFFFFL) == i; + } + + public static bool IsValidUint32(long i) + { + return (i & 0xFFFFFFFFL) == i; + } + + public static bool IsValidUint48(long i) + { + return (i & 0xFFFFFFFFFFFFL) == i; + } + + public static bool IsValidUint64(long i) + { + return true; + } + + public static bool IsSsl(TlsContext context) + { + return context.ServerVersion.IsSsl; + } + + public static bool IsTlsV11(TlsContext context) + { + return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion()); + } + + public static bool IsTlsV12(TlsContext context) + { + return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion()); + } + + public static void WriteUint8(byte i, Stream output) { - os.WriteByte(i); + output.WriteByte(i); } - internal static void WriteUint8(byte i, byte[] buf, int offset) + public static void WriteUint8(byte i, byte[] buf, int offset) { buf[offset] = i; } - internal static void WriteUint16(int i, Stream os) + public static void WriteUint16(int i, Stream output) { - os.WriteByte((byte)(i >> 8)); - os.WriteByte((byte)i); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); } - internal static void WriteUint16(int i, byte[] buf, int offset) + public 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) + public static void WriteUint24(int i, Stream output) { - os.WriteByte((byte)(i >> 16)); - os.WriteByte((byte)(i >> 8)); - os.WriteByte((byte)i); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); } - internal static void WriteUint24(int i, byte[] buf, int offset) + public static void WriteUint24(int i, byte[] buf, int offset) { buf[offset] = (byte)(i >> 16); buf[offset + 1] = (byte)(i >> 8); - buf[offset + 2] = (byte)(i); + buf[offset + 2] = (byte)i; + } + + public static void WriteUint32(long i, Stream output) + { + output.WriteByte((byte)(i >> 24)); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint32(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 24); + buf[offset + 1] = (byte)(i >> 16); + buf[offset + 2] = (byte)(i >> 8); + buf[offset + 3] = (byte)i; + } + + public static void WriteUint48(long i, Stream output) + { + output.WriteByte((byte)(i >> 40)); + output.WriteByte((byte)(i >> 32)); + output.WriteByte((byte)(i >> 24)); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint48(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 40); + buf[offset + 1] = (byte)(i >> 32); + buf[offset + 2] = (byte)(i >> 24); + buf[offset + 3] = (byte)(i >> 16); + buf[offset + 4] = (byte)(i >> 8); + buf[offset + 5] = (byte)i; } - internal static void WriteUint64(long i, Stream os) + public static void WriteUint64(long i, Stream output) { - 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); + output.WriteByte((byte)(i >> 56)); + output.WriteByte((byte)(i >> 48)); + output.WriteByte((byte)(i >> 40)); + output.WriteByte((byte)(i >> 32)); + output.WriteByte((byte)(i >> 24)); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); } - internal static void WriteUint64(long i, byte[] buf, int offset) + public static void WriteUint64(long i, byte[] buf, int offset) { buf[offset] = (byte)(i >> 56); buf[offset + 1] = (byte)(i >> 48); @@ -109,30 +232,39 @@ namespace Org.BouncyCastle.Crypto.Tls buf[offset + 4] = (byte)(i >> 24); buf[offset + 5] = (byte)(i >> 16); buf[offset + 6] = (byte)(i >> 8); - buf[offset + 7] = (byte)(i); + buf[offset + 7] = (byte)i; + } + + public static void WriteOpaque8(byte[] buf, Stream output) + { + WriteUint8((byte)buf.Length, output); + output.Write(buf, 0, buf.Length); } - internal static void WriteOpaque8(byte[] buf, Stream os) + public static void WriteOpaque16(byte[] buf, Stream output) { - WriteUint8((byte)buf.Length, os); - os.Write(buf, 0, buf.Length); + WriteUint16(buf.Length, output); + output.Write(buf, 0, buf.Length); } - internal static void WriteOpaque16(byte[] buf, Stream os) + public static void WriteOpaque24(byte[] buf, Stream output) { - WriteUint16(buf.Length, os); - os.Write(buf, 0, buf.Length); + WriteUint24(buf.Length, output); + output.Write(buf, 0, buf.Length); } - internal static void WriteOpaque24(byte[] buf, Stream os) + public static void WriteUint8Array(byte[] uints, Stream output) { - WriteUint24(buf.Length, os); - os.Write(buf, 0, buf.Length); + output.Write(uints, 0, uints.Length); } - internal static void WriteUint8Array(byte[] uints, Stream os) + public static void WriteUint8Array(byte[] uints, byte[] buf, int offset) { - os.Write(uints, 0, uints.Length); + for (int i = 0; i < uints.Length; ++i) + { + WriteUint8(uints[i], buf, offset); + ++offset; + } } public static void WriteUint8ArrayWithUint8Length(byte[] uints, Stream output) @@ -142,17 +274,70 @@ namespace Org.BouncyCastle.Crypto.Tls WriteUint8Array(uints, output); } - internal static void WriteUint16Array(int[] uints, Stream os) + public static void WriteUint8ArrayWithUint8Length(byte[] uints, byte[] buf, int offset) + { + CheckUint8(uints.Length); + WriteUint8((byte)uints.Length, buf, offset); + WriteUint8Array(uints, buf, offset + 1); + } + + public static void WriteUint16Array(int[] uints, Stream output) { for (int i = 0; i < uints.Length; ++i) { - WriteUint16(uints[i], os); + WriteUint16(uints[i], output); } } - internal static byte ReadUint8(Stream inStr) + public static void WriteUint16Array(int[] uints, byte[] buf, int offset) { - int i = inStr.ReadByte(); + for (int i = 0; i < uints.Length; ++i) + { + WriteUint16(uints[i], buf, offset); + offset += 2; + } + } + + public static void WriteUint16ArrayWithUint16Length(int[] uints, Stream output) + { + int length = 2 * uints.Length; + CheckUint16(length); + WriteUint16(length, output); + WriteUint16Array(uints, output); + } + + public static void WriteUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset) + { + int length = 2 * uints.Length; + CheckUint16(length); + WriteUint16(length, buf, offset); + WriteUint16Array(uints, buf, offset + 2); + } + + public static byte[] EncodeOpaque8(byte[] buf) + { + CheckUint8(buf.Length); + return Arrays.Prepend(buf, (byte)buf.Length); + } + + public static byte[] EncodeUint8ArrayWithUint8Length(byte[] uints) + { + byte[] result = new byte[1 + uints.Length]; + WriteUint8ArrayWithUint8Length(uints, result, 0); + return result; + } + + public static byte[] EncodeUint16ArrayWithUint16Length(int[] uints) + { + int length = 2 * uints.Length; + byte[] result = new byte[2 + length]; + WriteUint16ArrayWithUint16Length(uints, result, 0); + return result; + } + + public static byte ReadUint8(Stream input) + { + int i = input.ReadByte(); if (i < 0) { throw new EndOfStreamException(); @@ -160,10 +345,15 @@ namespace Org.BouncyCastle.Crypto.Tls return (byte)i; } - internal static int ReadUint16(Stream inStr) + public static byte ReadUint8(byte[] buf, int offset) { - int i1 = inStr.ReadByte(); - int i2 = inStr.ReadByte(); + return buf[offset]; + } + + public static int ReadUint16(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); if ((i1 | i2) < 0) { throw new EndOfStreamException(); @@ -171,11 +361,18 @@ namespace Org.BouncyCastle.Crypto.Tls return i1 << 8 | i2; } - internal static int ReadUint24(Stream inStr) + public static int ReadUint16(byte[] buf, int offset) { - int i1 = inStr.ReadByte(); - int i2 = inStr.ReadByte(); - int i3 = inStr.ReadByte(); + uint n = (uint)buf[offset] << 8; + n |= (uint)buf[++offset]; + return (int)n; + } + + public static int ReadUint24(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + int i3 = input.ReadByte(); if ((i1 | i2 | i3) < 0) { throw new EndOfStreamException(); @@ -183,6 +380,63 @@ namespace Org.BouncyCastle.Crypto.Tls return (i1 << 16) | (i2 << 8) | i3; } + public static int ReadUint24(byte[] buf, int offset) + { + uint n = (uint)buf[offset] << 16; + n |= (uint)buf[++offset] << 8; + n |= (uint)buf[++offset]; + return (int)n; + } + + public static long ReadUint32(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + int i3 = input.ReadByte(); + int i4 = input.ReadByte(); + if (i4 < 0) + { + throw new EndOfStreamException(); + } + return (long)(uint)((i1 << 24) | (i2 << 16) | (i3 << 8) | i4); + } + + public static long ReadUint32(byte[] buf, int offset) + { + uint n = (uint)buf[offset] << 24; + n |= (uint)buf[++offset] << 16; + n |= (uint)buf[++offset] << 8; + n |= (uint)buf[++offset]; + return (long)n; + } + + public static long ReadUint48(Stream input) + { + int hi = ReadUint24(input); + int lo = ReadUint24(input); + return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL); + } + + public static long ReadUint48(byte[] buf, int offset) + { + int hi = ReadUint24(buf, offset); + int lo = ReadUint24(buf, offset + 3); + return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL); + } + + public static byte[] ReadAllOrNothing(int length, Stream input) + { + if (length < 1) + return EmptyBytes; + byte[] buf = new byte[length]; + int read = Streams.ReadFully(input, buf); + if (read == 0) + return null; + if (read != length) + throw new EndOfStreamException(); + return buf; + } + public static byte[] ReadFully(int length, Stream input) { if (length < 1) @@ -193,25 +447,25 @@ namespace Org.BouncyCastle.Crypto.Tls return buf; } - internal static void ReadFully(byte[] buf, Stream inStr) + public static void ReadFully(byte[] buf, Stream input) { - if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length) + if (Streams.ReadFully(input, buf, 0, buf.Length) < buf.Length) throw new EndOfStreamException(); } - internal static byte[] ReadOpaque8(Stream inStr) + public static byte[] ReadOpaque8(Stream input) { - byte length = ReadUint8(inStr); + byte length = ReadUint8(input); byte[] bytes = new byte[length]; - ReadFully(bytes, inStr); + ReadFully(bytes, input); return bytes; } - internal static byte[] ReadOpaque16(Stream inStr) + public static byte[] ReadOpaque16(Stream input) { - int length = ReadUint16(inStr); + int length = ReadUint16(input); byte[] bytes = new byte[length]; - ReadFully(bytes, inStr); + ReadFully(bytes, input); return bytes; } @@ -221,22 +475,56 @@ namespace Org.BouncyCastle.Crypto.Tls return ReadFully(length, input); } - internal static void CheckVersion(byte[] readVersion) + public static byte[] ReadUint8Array(int count, Stream input) { - if ((readVersion[0] != 3) || (readVersion[1] != 1)) + byte[] uints = new byte[count]; + for (int i = 0; i < count; ++i) { - throw new TlsFatalAlert(AlertDescription.protocol_version); + uints[i] = ReadUint8(input); } + return uints; } - internal static void CheckVersion(Stream inStr) + public static int[] ReadUint16Array(int count, Stream input) { - int i1 = inStr.ReadByte(); - int i2 = inStr.ReadByte(); - if ((i1 != 3) || (i2 != 1)) + int[] uints = new int[count]; + for (int i = 0; i < count; ++i) { - throw new TlsFatalAlert(AlertDescription.protocol_version); + uints[i] = ReadUint16(input); } + return uints; + } + + public static ProtocolVersion ReadVersion(byte[] buf, int offset) + { + return ProtocolVersion.Get(buf[offset], buf[offset + 1]); + } + + public static ProtocolVersion ReadVersion(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + if (i2 < 0) + { + throw new EndOfStreamException(); + } + return ProtocolVersion.Get(i1, i2); + } + + public static int ReadVersionRaw(byte[] buf, int offset) + { + return (buf[offset] << 8) | buf[offset + 1]; + } + + public static int ReadVersionRaw(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + if (i2 < 0) + { + throw new EndOfStreamException(); + } + return (i1 << 8) | i2; } public static Asn1Object ReadAsn1Object(byte[] encoding) @@ -263,7 +551,7 @@ namespace Org.BouncyCastle.Crypto.Tls return result; } - internal static void WriteGmtUnixTime(byte[] buf, int offset) + public static void WriteGmtUnixTime(byte[] buf, int offset) { int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L); buf[offset] = (byte)(t >> 24); @@ -272,16 +560,152 @@ namespace Org.BouncyCastle.Crypto.Tls buf[offset + 3] = (byte)t; } - internal static void WriteVersion(Stream os) + public static void WriteVersion(ProtocolVersion version, Stream output) + { + output.WriteByte((byte)version.MajorVersion); + output.WriteByte((byte)version.MinorVersion); + } + + public static void WriteVersion(ProtocolVersion version, byte[] buf, int offset) + { + buf[offset] = (byte)version.MajorVersion; + buf[offset + 1] = (byte)version.MinorVersion; + } + + public static IList GetDefaultDssSignatureAlgorithms() + { + return VectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa)); + } + + public static IList GetDefaultECDsaSignatureAlgorithms() + { + return VectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)); + } + + public static IList GetDefaultRsaSignatureAlgorithms() + { + return VectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa)); + } + + public static byte[] GetExtensionData(IDictionary extensions, int extensionType) + { + return extensions == null ? null : (byte[])extensions[extensionType]; + } + + public static IList GetDefaultSupportedSignatureAlgorithms() + { + byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha1, HashAlgorithm.sha224, HashAlgorithm.sha256, + HashAlgorithm.sha384, HashAlgorithm.sha512 }; + byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa, SignatureAlgorithm.dsa, + SignatureAlgorithm.ecdsa }; + + IList result = Platform.CreateArrayList(); + for (int i = 0; i < signatureAlgorithms.Length; ++i) + { + for (int j = 0; j < hashAlgorithms.Length; ++j) + { + result.Add(new SignatureAndHashAlgorithm(hashAlgorithms[j], signatureAlgorithms[i])); + } + } + return result; + } + + public static SignatureAndHashAlgorithm GetSignatureAndHashAlgorithm(TlsContext context, + TlsSignerCredentials signerCredentials) + { + SignatureAndHashAlgorithm signatureAndHashAlgorithm = null; + if (IsTlsV12(context)) + { + signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm; + if (signatureAndHashAlgorithm == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + return signatureAndHashAlgorithm; + } + + public static bool HasExpectedEmptyExtensionData(IDictionary extensions, int extensionType, + byte alertDescription) + { + byte[] extension_data = GetExtensionData(extensions, extensionType); + if (extension_data == null) + return false; + if (extension_data.Length != 0) + throw new TlsFatalAlert(alertDescription); + return true; + } + + public static TlsSession ImportSession(byte[] sessionID, SessionParameters sessionParameters) + { + return new TlsSessionImpl(sessionID, sessionParameters); + } + + public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion) + { + return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(clientVersion.GetEquivalentTLSVersion()); + } + + /** + * Add a 'signature_algorithms' extension to existing extensions. + * + * @param extensions A {@link Hashtable} to add the extension to. + * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @throws IOException + */ + public static void AddSignatureAlgorithmsExtension(IDictionary extensions, IList supportedSignatureAlgorithms) + { + extensions[ExtensionType.signature_algorithms] = CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms); + } + + /** + * Get a 'signature_algorithms' extension from extensions. + * + * @param extensions A {@link Hashtable} to get the extension from, if it is present. + * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null. + * @throws IOException + */ + public static IList GetSignatureAlgorithmsExtension(IDictionary extensions) { - os.WriteByte(3); - os.WriteByte(1); + byte[] extensionData = GetExtensionData(extensions, ExtensionType.signature_algorithms); + return extensionData == null ? null : ReadSignatureAlgorithmsExtension(extensionData); } - internal static void WriteVersion(byte[] buf, int offset) + /** + * Create a 'signature_algorithms' extension value. + * + * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @return A byte array suitable for use as an extension value. + * @throws IOException + */ + public static byte[] CreateSignatureAlgorithmsExtension(IList supportedSignatureAlgorithms) { - buf[offset] = 3; - buf[offset + 1] = 1; + MemoryStream buf = new MemoryStream(); + + // supported_signature_algorithms + EncodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf); + + return buf.ToArray(); + } + + /** + * Read 'signature_algorithms' extension data. + * + * @param extensionData The extension data. + * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @throws IOException + */ + public static IList ReadSignatureAlgorithmsExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + // supported_signature_algorithms + IList supported_signature_algorithms = ParseSupportedSignatureAlgorithms(false, buf); + + TlsProtocol.AssertEmpty(buf); + + return supported_signature_algorithms; } public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous, @@ -295,8 +719,8 @@ namespace Org.BouncyCastle.Crypto.Tls // supported_signature_algorithms int length = 2 * supportedSignatureAlgorithms.Count; - TlsUtilities.CheckUint16(length); - TlsUtilities.WriteUint16(length, output); + CheckUint16(length); + WriteUint16(length, output); foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms) { @@ -313,58 +737,76 @@ namespace Org.BouncyCastle.Crypto.Tls } } - private static void hmac_hash(IDigest digest, byte[] secret, byte[] seed, byte[] output) + public static IList ParseSupportedSignatureAlgorithms(bool allowAnonymous, Stream input) { - HMac mac = new HMac(digest); - mac.Init(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++) + // supported_signature_algorithms + int length = ReadUint16(input); + if (length < 2 || (length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + int count = length / 2; + IList supportedSignatureAlgorithms = Platform.CreateArrayList(count); + for (int i = 0; i < count; ++i) { - mac.BlockUpdate(a, 0, a.Length); - mac.DoFinal(buf, 0); - a = buf; - 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))); + SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.Parse(input); + if (!allowAnonymous && entry.Signature == SignatureAlgorithm.anonymous) + { + /* + * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used + * in Section 7.4.3. It MUST NOT appear in this extension. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + supportedSignatureAlgorithms.Add(entry); } + return supportedSignatureAlgorithms; } - internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size) + public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size) { - byte[] label = Strings.ToAsciiByteArray(asciiLabel); + ProtocolVersion version = context.ServerVersion; - 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); + if (version.IsSsl) + throw new InvalidOperationException("No PRF available for SSLv3 session"); - byte[] ls = Concat(label, seed); + byte[] label = Strings.ToByteArray(asciiLabel); + byte[] labelSeed = Concat(label, seed); + + int prfAlgorithm = context.SecurityParameters.PrfAlgorithm; + if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy) + return PRF_legacy(secret, label, labelSeed, size); + + IDigest prfDigest = CreatePrfHash(prfAlgorithm); byte[] buf = new byte[size]; - byte[] prf = new byte[size]; - hmac_hash(new MD5Digest(), s1, ls, prf); - hmac_hash(new Sha1Digest(), s2, ls, buf); - for (int i = 0; i < size; i++) - { - buf[i] ^= prf[i]; - } + HMacHash(prfDigest, secret, labelSeed, buf); return buf; } - internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size) + public static byte[] PRF_legacy(byte[] secret, string asciiLabel, byte[] seed, int size) { - byte[] label = Strings.ToAsciiByteArray(asciiLabel); + byte[] label = Strings.ToByteArray(asciiLabel); byte[] labelSeed = Concat(label, seed); - byte[] buf = new byte[size]; - hmac_hash(digest, secret, labelSeed, buf); - return buf; + return PRF_legacy(secret, label, labelSeed, size); + } + + internal static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size) + { + 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[] b1 = new byte[size]; + byte[] b2 = new byte[size]; + HMacHash(CreateHash(HashAlgorithm.md5), s1, labelSeed, b1); + HMacHash(CreateHash(HashAlgorithm.sha1), s2, labelSeed, b2); + for (int i = 0; i < size; i++) + { + b1[i] ^= b2[i]; + } + return b1; } internal static byte[] Concat(byte[] a, byte[] b) @@ -375,6 +817,27 @@ namespace Org.BouncyCastle.Crypto.Tls return c; } + internal static void HMacHash(IDigest digest, byte[] secret, byte[] seed, byte[] output) + { + HMac mac = new HMac(digest); + mac.Init(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.BlockUpdate(a, 0, a.Length); + mac.DoFinal(buf, 0); + a = buf; + 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 ValidateKeyUsage(X509CertificateStructure c, int keyUsageBits) { X509Extensions exts = c.TbsCertificate.Extensions; @@ -386,11 +849,1351 @@ namespace Org.BouncyCastle.Crypto.Tls DerBitString ku = KeyUsage.GetInstance(ext); int bits = ku.GetBytes()[0]; if ((bits & keyUsageBits) != keyUsageBits) - { throw new TlsFatalAlert(AlertDescription.certificate_unknown); - } } } } + + internal static byte[] CalculateKeyBlock(TlsContext context, int size) + { + SecurityParameters securityParameters = context.SecurityParameters; + byte[] master_secret = securityParameters.MasterSecret; + byte[] seed = Concat(securityParameters.ServerRandom, securityParameters.ClientRandom); + + if (IsSsl(context)) + return CalculateKeyBlock_Ssl(master_secret, seed, size); + + return PRF(context, master_secret, ExporterLabel.key_expansion, seed, size); + } + + internal static byte[] CalculateKeyBlock_Ssl(byte[] master_secret, byte[] random, int size) + { + IDigest md5 = CreateHash(HashAlgorithm.md5); + IDigest sha1 = CreateHash(HashAlgorithm.sha1); + int md5Size = md5.GetDigestSize(); + byte[] shatmp = new byte[sha1.GetDigestSize()]; + byte[] tmp = new byte[size + md5Size]; + + int i = 0, pos = 0; + while (pos < size) + { + byte[] ssl3Const = SSL3_CONST[i]; + + sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length); + sha1.BlockUpdate(master_secret, 0, master_secret.Length); + sha1.BlockUpdate(random, 0, random.Length); + sha1.DoFinal(shatmp, 0); + + md5.BlockUpdate(master_secret, 0, master_secret.Length); + md5.BlockUpdate(shatmp, 0, shatmp.Length); + md5.DoFinal(tmp, pos); + + pos += md5Size; + ++i; + } + + return Arrays.CopyOfRange(tmp, 0, size); + } + + internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_master_secret) + { + SecurityParameters securityParameters = context.SecurityParameters; + + byte[] seed = securityParameters.extendedMasterSecret + ? securityParameters.SessionHash + : Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); + + if (IsSsl(context)) + return CalculateMasterSecret_Ssl(pre_master_secret, seed); + + string asciiLabel = securityParameters.extendedMasterSecret + ? ExporterLabel.extended_master_secret + : ExporterLabel.master_secret; + + return PRF(context, pre_master_secret, asciiLabel, seed, 48); + } + + internal static byte[] CalculateMasterSecret_Ssl(byte[] pre_master_secret, byte[] random) + { + IDigest md5 = CreateHash(HashAlgorithm.md5); + IDigest sha1 = CreateHash(HashAlgorithm.sha1); + int md5Size = md5.GetDigestSize(); + byte[] shatmp = new byte[sha1.GetDigestSize()]; + + byte[] rval = new byte[md5Size * 3]; + int pos = 0; + + for (int i = 0; i < 3; ++i) + { + byte[] ssl3Const = SSL3_CONST[i]; + + sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length); + sha1.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length); + sha1.BlockUpdate(random, 0, random.Length); + sha1.DoFinal(shatmp, 0); + + md5.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length); + md5.BlockUpdate(shatmp, 0, shatmp.Length); + md5.DoFinal(rval, pos); + + pos += md5Size; + } + + return rval; + } + + internal static byte[] CalculateVerifyData(TlsContext context, string asciiLabel, byte[] handshakeHash) + { + if (IsSsl(context)) + return handshakeHash; + + SecurityParameters securityParameters = context.SecurityParameters; + byte[] master_secret = securityParameters.MasterSecret; + int verify_data_length = securityParameters.VerifyDataLength; + + return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length); + } + + public static IDigest CreateHash(byte hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return new MD5Digest(); + case HashAlgorithm.sha1: + return new Sha1Digest(); + case HashAlgorithm.sha224: + return new Sha224Digest(); + case HashAlgorithm.sha256: + return new Sha256Digest(); + case HashAlgorithm.sha384: + return new Sha384Digest(); + case HashAlgorithm.sha512: + return new Sha512Digest(); + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + + public static IDigest CreateHash(SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + return signatureAndHashAlgorithm == null + ? new CombinedHash() + : CreateHash(signatureAndHashAlgorithm.Hash); + } + + public static IDigest CloneHash(byte hashAlgorithm, IDigest hash) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return new MD5Digest((MD5Digest)hash); + case HashAlgorithm.sha1: + return new Sha1Digest((Sha1Digest)hash); + case HashAlgorithm.sha224: + return new Sha224Digest((Sha224Digest)hash); + case HashAlgorithm.sha256: + return new Sha256Digest((Sha256Digest)hash); + case HashAlgorithm.sha384: + return new Sha384Digest((Sha384Digest)hash); + case HashAlgorithm.sha512: + return new Sha512Digest((Sha512Digest)hash); + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + + public static IDigest CreatePrfHash(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + return new CombinedHash(); + default: + return CreateHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm)); + } + } + + public static IDigest ClonePrfHash(int prfAlgorithm, IDigest hash) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + return new CombinedHash((CombinedHash)hash); + default: + return CloneHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm), hash); + } + } + + public static byte GetHashAlgorithmForPrfAlgorithm(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + throw new ArgumentException("legacy PRF not a valid algorithm", "prfAlgorithm"); + case PrfAlgorithm.tls_prf_sha256: + return HashAlgorithm.sha256; + case PrfAlgorithm.tls_prf_sha384: + return HashAlgorithm.sha384; + default: + throw new ArgumentException("unknown PrfAlgorithm", "prfAlgorithm"); + } + } + + public static DerObjectIdentifier GetOidForHashAlgorithm(byte hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return PkcsObjectIdentifiers.MD5; + case HashAlgorithm.sha1: + return X509ObjectIdentifiers.IdSha1; + case HashAlgorithm.sha224: + return NistObjectIdentifiers.IdSha224; + case HashAlgorithm.sha256: + return NistObjectIdentifiers.IdSha256; + case HashAlgorithm.sha384: + return NistObjectIdentifiers.IdSha384; + case HashAlgorithm.sha512: + return NistObjectIdentifiers.IdSha512; + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + + internal static short GetClientCertificateType(Certificate clientCertificate, Certificate serverCertificate) + { + if (clientCertificate.IsEmpty) + return -1; + + X509CertificateStructure x509Cert = clientCertificate.GetCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + try + { + AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo); + if (publicKey.IsPrivate) + throw new TlsFatalAlert(AlertDescription.internal_error); + + /* + * TODO RFC 5246 7.4.6. The certificates MUST be signed using an acceptable hash/ + * signature algorithm pair, as described in Section 7.4.4. Note that this relaxes the + * constraints on certificate-signing algorithms found in prior versions of TLS. + */ + + /* + * RFC 5246 7.4.6. Client Certificate + */ + + /* + * RSA public key; the certificate MUST allow the key to be used for signing with the + * signature scheme and hash algorithm that will be employed in the certificate verify + * message. + */ + if (publicKey is RsaKeyParameters) + { + ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + return ClientCertificateType.rsa_sign; + } + + /* + * DSA public key; the certificate MUST allow the key to be used for signing with the + * hash algorithm that will be employed in the certificate verify message. + */ + if (publicKey is DsaPublicKeyParameters) + { + ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + return ClientCertificateType.dss_sign; + } + + /* + * ECDSA-capable public key; the certificate MUST allow the key to be used for signing + * with the hash algorithm that will be employed in the certificate verify message; the + * public key MUST use a curve and point format supported by the server. + */ + if (publicKey is ECPublicKeyParameters) + { + ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + // TODO Check the curve and point format + return ClientCertificateType.ecdsa_sign; + } + + // TODO Add support for ClientCertificateType.*_fixed_* + + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + } + + internal static void TrackHashAlgorithms(TlsHandshakeHash handshakeHash, IList supportedSignatureAlgorithms) + { + if (supportedSignatureAlgorithms != null) + { + foreach (SignatureAndHashAlgorithm signatureAndHashAlgorithm in supportedSignatureAlgorithms) + { + byte hashAlgorithm = signatureAndHashAlgorithm.Hash; + handshakeHash.TrackHashAlgorithm(hashAlgorithm); + } + } + } + + public static bool HasSigningCapability(byte clientCertificateType) + { + switch (clientCertificateType) + { + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + case ClientCertificateType.rsa_sign: + return true; + default: + return false; + } + } + + public static TlsSigner CreateTlsSigner(byte clientCertificateType) + { + switch (clientCertificateType) + { + case ClientCertificateType.dss_sign: + return new TlsDssSigner(); + case ClientCertificateType.ecdsa_sign: + return new TlsECDsaSigner(); + case ClientCertificateType.rsa_sign: + return new TlsRsaSigner(); + default: + throw new ArgumentException("not a type with signing capability", "clientCertificateType"); + } + } + + internal static readonly byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54}; + internal static readonly byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52}; + + // SSL3 magic mix constants ("A", "BB", "CCC", ...) + internal static readonly byte[][] SSL3_CONST = GenSsl3Const(); + + private static byte[][] GenSsl3Const() + { + int n = 10; + byte[][] arr = new byte[n][]; + for (int i = 0; i < n; i++) + { + byte[] b = new byte[i + 1]; + Arrays.Fill(b, (byte)('A' + i)); + arr[i] = b; + } + return arr; + } + + private static IList VectorOfOne(object obj) + { + IList v = Platform.CreateArrayList(1); + v.Add(obj); + return v; + } + + public static int GetCipherType(int ciphersuite) + { + switch (GetEncryptionAlgorithm(ciphersuite)) + { + case EncryptionAlgorithm.AES_128_GCM: + case EncryptionAlgorithm.AES_256_GCM: + case EncryptionAlgorithm.AES_128_CCM: + case EncryptionAlgorithm.AES_128_CCM_8: + case EncryptionAlgorithm.AES_256_CCM: + case EncryptionAlgorithm.AES_256_CCM_8: + case EncryptionAlgorithm.CAMELLIA_128_GCM: + case EncryptionAlgorithm.CAMELLIA_256_GCM: + case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305: + return CipherType.aead; + + case EncryptionAlgorithm.RC2_CBC_40: + case EncryptionAlgorithm.IDEA_CBC: + case EncryptionAlgorithm.DES40_CBC: + case EncryptionAlgorithm.DES_CBC: + case EncryptionAlgorithm.cls_3DES_EDE_CBC: + case EncryptionAlgorithm.AES_128_CBC: + case EncryptionAlgorithm.AES_256_CBC: + case EncryptionAlgorithm.CAMELLIA_128_CBC: + case EncryptionAlgorithm.CAMELLIA_256_CBC: + case EncryptionAlgorithm.SEED_CBC: + return CipherType.block; + + case EncryptionAlgorithm.RC4_40: + case EncryptionAlgorithm.RC4_128: + case EncryptionAlgorithm.ESTREAM_SALSA20: + case EncryptionAlgorithm.SALSA20: + return CipherType.stream; + + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static int GetEncryptionAlgorithm(int ciphersuite) + { + switch (ciphersuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + return EncryptionAlgorithm.cls_3DES_EDE_CBC; + + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return EncryptionAlgorithm.AEAD_CHACHA20_POLY1305; + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + return EncryptionAlgorithm.AES_128_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + return EncryptionAlgorithm.AES_128_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + return EncryptionAlgorithm.AES_128_CCM; + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + return EncryptionAlgorithm.AES_128_CCM_8; + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + return EncryptionAlgorithm.AES_128_GCM; + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return EncryptionAlgorithm.AES_256_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + return EncryptionAlgorithm.AES_256_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return EncryptionAlgorithm.AES_256_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + return EncryptionAlgorithm.AES_256_CCM; + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + return EncryptionAlgorithm.AES_256_CCM_8; + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + return EncryptionAlgorithm.AES_256_GCM; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + return EncryptionAlgorithm.CAMELLIA_128_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + return EncryptionAlgorithm.CAMELLIA_128_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + return EncryptionAlgorithm.CAMELLIA_128_GCM; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + return EncryptionAlgorithm.CAMELLIA_256_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + return EncryptionAlgorithm.CAMELLIA_256_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + return EncryptionAlgorithm.CAMELLIA_256_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + return EncryptionAlgorithm.CAMELLIA_256_GCM; + + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + return EncryptionAlgorithm.ESTREAM_SALSA20; + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return EncryptionAlgorithm.RC4_128; + + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + return EncryptionAlgorithm.RC4_128; + + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + return EncryptionAlgorithm.SALSA20; + + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return EncryptionAlgorithm.SEED_CBC; + + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static int GetKeyExchangeAlgorithm(int ciphersuite) + { + switch (ciphersuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + return KeyExchangeAlgorithm.DH_DSS; + + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + return KeyExchangeAlgorithm.DH_RSA; + + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + return KeyExchangeAlgorithm.DHE_DSS; + + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + return KeyExchangeAlgorithm.DHE_PSK; + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + return KeyExchangeAlgorithm.DHE_RSA; + + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return KeyExchangeAlgorithm.ECDH_ECDSA; + + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + return KeyExchangeAlgorithm.ECDH_RSA; + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + return KeyExchangeAlgorithm.ECDHE_ECDSA; + + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + return KeyExchangeAlgorithm.ECDHE_PSK; + + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + return KeyExchangeAlgorithm.ECDHE_RSA; + + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + return KeyExchangeAlgorithm.PSK; + + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return KeyExchangeAlgorithm.RSA; + + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + return KeyExchangeAlgorithm.RSA_PSK; + + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return KeyExchangeAlgorithm.SRP; + + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + return KeyExchangeAlgorithm.SRP_DSS; + + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + return KeyExchangeAlgorithm.SRP_RSA; + + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static int GetMacAlgorithm(int ciphersuite) + { + switch (ciphersuite) + { + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + return MacAlgorithm.cls_null; + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return MacAlgorithm.hmac_md5; + + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return MacAlgorithm.hmac_sha1; + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return MacAlgorithm.hmac_sha256; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + return MacAlgorithm.hmac_sha384; + + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static ProtocolVersion GetMinimumVersion(int ciphersuite) + { + switch (ciphersuite) + { + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return ProtocolVersion.TLSv12; + + default: + return ProtocolVersion.SSLv3; + } + } + + public static bool IsAeadCipherSuite(int ciphersuite) + { + return CipherType.aead == GetCipherType(ciphersuite); + } + + public static bool IsBlockCipherSuite(int ciphersuite) + { + return CipherType.block == GetCipherType(ciphersuite); + } + + public static bool IsStreamCipherSuite(int ciphersuite) + { + return CipherType.stream == GetCipherType(ciphersuite); + } + + public static bool IsValidCipherSuiteForVersion(int cipherSuite, ProtocolVersion serverVersion) + { + return GetMinimumVersion(cipherSuite).IsEqualOrEarlierVersionOf(serverVersion.GetEquivalentTLSVersion()); + } } } diff --git a/crypto/src/crypto/tls/UrlAndHash.cs b/crypto/src/crypto/tls/UrlAndHash.cs new file mode 100644
index 000000000..9ffd2cbf8 --- /dev/null +++ b/crypto/src/crypto/tls/UrlAndHash.cs
@@ -0,0 +1,94 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 6066 5. + */ + public class UrlAndHash + { + protected readonly string mUrl; + protected readonly byte[] mSha1Hash; + + public UrlAndHash(string url, byte[] sha1Hash) + { + if (url == null || url.Length < 1 || url.Length >= (1 << 16)) + throw new ArgumentException("must have length from 1 to (2^16 - 1)", "url"); + if (sha1Hash != null && sha1Hash.Length != 20) + throw new ArgumentException("must have length == 20, if present", "sha1Hash"); + + this.mUrl = url; + this.mSha1Hash = sha1Hash; + } + + public virtual string Url + { + get { return mUrl; } + } + + public virtual byte[] Sha1Hash + { + get { return mSha1Hash; } + } + + /** + * Encode this {@link UrlAndHash} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + byte[] urlEncoding = Strings.ToByteArray(this.mUrl); + TlsUtilities.WriteOpaque16(urlEncoding, output); + + if (this.mSha1Hash == null) + { + TlsUtilities.WriteUint8(0, output); + } + else + { + TlsUtilities.WriteUint8(1, output); + output.Write(this.mSha1Hash, 0, this.mSha1Hash.Length); + } + } + + /** + * Parse a {@link UrlAndHash} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link UrlAndHash} object. + * @throws IOException + */ + public static UrlAndHash Parse(TlsContext context, Stream input) + { + byte[] urlEncoding = TlsUtilities.ReadOpaque16(input); + if (urlEncoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + string url = Strings.FromByteArray(urlEncoding); + + byte[] sha1Hash = null; + byte padding = TlsUtilities.ReadUint8(input); + switch (padding) + { + case 0: + if (TlsUtilities.IsTlsV12(context)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + break; + case 1: + sha1Hash = TlsUtilities.ReadFully(20, input); + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return new UrlAndHash(url, sha1Hash); + } + } +} diff --git a/crypto/src/crypto/tls/UseSrtpData.cs b/crypto/src/crypto/tls/UseSrtpData.cs new file mode 100644
index 000000000..fe8f8accb --- /dev/null +++ b/crypto/src/crypto/tls/UseSrtpData.cs
@@ -0,0 +1,56 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5764 4.1.1 + */ + public class UseSrtpData + { + protected readonly int[] mProtectionProfiles; + protected readonly byte[] mMki; + + /** + * @param protectionProfiles see {@link SrtpProtectionProfile} for valid constants. + * @param mki valid lengths from 0 to 255. + */ + public UseSrtpData(int[] protectionProfiles, byte[] mki) + { + if (protectionProfiles == null || protectionProfiles.Length < 1 + || protectionProfiles.Length >= (1 << 15)) + { + throw new ArgumentException("must have length from 1 to (2^15 - 1)", "protectionProfiles"); + } + + if (mki == null) + { + mki = TlsUtilities.EmptyBytes; + } + else if (mki.Length > 255) + { + throw new ArgumentException("cannot be longer than 255 bytes", "mki"); + } + + this.mProtectionProfiles = protectionProfiles; + this.mMki = mki; + } + + /** + * @return see {@link SrtpProtectionProfile} for valid constants. + */ + public virtual int[] ProtectionProfiles + { + get { return mProtectionProfiles; } + } + + /** + * @return valid lengths from 0 to 255. + */ + public virtual byte[] Mki + { + get { return mMki; } + } + } +} diff --git a/crypto/src/crypto/tls/UserMappingType.cs b/crypto/src/crypto/tls/UserMappingType.cs
index 6e6d40a58..6cff51736 100644 --- a/crypto/src/crypto/tls/UserMappingType.cs +++ b/crypto/src/crypto/tls/UserMappingType.cs
@@ -8,6 +8,6 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 4681 */ - public const short upn_domain_hint = 64; + public const byte upn_domain_hint = 64; } } diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index 087cb7cea..dc00ab450 100644 --- a/crypto/src/crypto/util/Pack.cs +++ b/crypto/src/crypto/util/Pack.cs
@@ -117,6 +117,22 @@ namespace Org.BouncyCastle.Crypto.Utilities UInt32_To_BE((uint)(n), bs, off + 4); } + internal static byte[] UInt64_To_BE(ulong[] ns) + { + byte[] bs = new byte[8 * ns.Length]; + UInt64_To_BE(ns, bs, 0); + return bs; + } + + internal static void UInt64_To_BE(ulong[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.Length; ++i) + { + UInt64_To_BE(ns[i], bs, off); + off += 8; + } + } + internal static ulong BE_To_UInt64(byte[] bs) { uint hi = BE_To_UInt32(bs); @@ -131,6 +147,15 @@ namespace Org.BouncyCastle.Crypto.Utilities return ((ulong)hi << 32) | (ulong)lo; } + internal static void BE_To_UInt64(byte[] bs, int off, ulong[] ns) + { + for (int i = 0; i < ns.Length; ++i) + { + ns[i] = BE_To_UInt64(bs, off); + off += 8; + } + } + internal static void UInt16_To_LE(ushort n, byte[] bs) { bs[0] = (byte)(n); diff --git a/crypto/src/math/Primes.cs b/crypto/src/math/Primes.cs new file mode 100644
index 000000000..420c3cc5a --- /dev/null +++ b/crypto/src/math/Primes.cs
@@ -0,0 +1,584 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math +{ + /** + * Utility methods for generating primes and testing for primality. + */ + public abstract class Primes + { + private static readonly BigInteger One = BigInteger.One; + private static readonly BigInteger Two = BigInteger.Two; + private static readonly BigInteger Three = BigInteger.Three; + + /** + * Used to return the output from the + * {@linkplain Primes#enhancedMRProbablePrimeTest(BigInteger, SecureRandom, int) Enhanced + * Miller-Rabin Probabilistic Primality Test} + */ + public class MROutput + { + internal static MROutput ProbablyPrime() + { + return new MROutput(false, null); + } + + internal static MROutput ProvablyCompositeWithFactor(BigInteger factor) + { + return new MROutput(true, factor); + } + + internal static MROutput ProvablyCompositeNotPrimePower() + { + return new MROutput(true, null); + } + + private readonly bool mProvablyComposite; + private readonly BigInteger mFactor; + + private MROutput(bool provablyComposite, BigInteger factor) + { + this.mProvablyComposite = provablyComposite; + this.mFactor = factor; + } + + public BigInteger Factor + { + get { return mFactor; } + } + + public bool IsProvablyComposite + { + get { return mProvablyComposite; } + } + + public bool IsNotPrimePower + { + get { return mProvablyComposite && mFactor == null; } + } + } + + /** + * Used to return the output from the {@linkplain Primes#generateSTRandomPrime(Digest, int, byte[]) Shawe-Taylor Random_Prime Routine} + */ + public class STOutput + { + private readonly BigInteger mPrime; + private readonly byte[] mPrimeSeed; + private readonly int mPrimeGenCounter; + + internal STOutput(BigInteger prime, byte[] primeSeed, int primeGenCounter) + { + this.mPrime = prime; + this.mPrimeSeed = primeSeed; + this.mPrimeGenCounter = primeGenCounter; + } + + public BigInteger Prime + { + get { return mPrime; } + } + + public byte[] PrimeSeed + { + get { return mPrimeSeed; } + } + + public int PrimeGenCounter + { + get { return mPrimeGenCounter; } + } + } + + /** + * FIPS 186-4 C.6 Shawe-Taylor Random_Prime Routine + * + * Construct a provable prime number using a hash function. + * + * @param hash + * the {@link Digest} instance to use (as "Hash()"). Cannot be null. + * @param length + * the length (in bits) of the prime to be generated. Must be at least 2. + * @param inputSeed + * the seed to be used for the generation of the requested prime. Cannot be null or + * empty. + * @return an {@link STOutput} instance containing the requested prime. + */ + public static STOutput GenerateSTRandomPrime(IDigest hash, int length, byte[] inputSeed) + { + if (hash == null) + throw new ArgumentNullException("hash"); + if (length < 2) + throw new ArgumentException("must be >= 2", "length"); + if (inputSeed == null) + throw new ArgumentNullException("inputSeed"); + if (inputSeed.Length == 0) + throw new ArgumentException("cannot be empty", "inputSeed"); + + return ImplSTRandomPrime(hash, length, Arrays.Clone(inputSeed)); + } + + /** + * FIPS 186-4 C.3.2 Enhanced Miller-Rabin Probabilistic Primality Test + * + * Run several iterations of the Miller-Rabin algorithm with randomly-chosen bases. This is an + * alternative to {@link #isMRProbablePrime(BigInteger, SecureRandom, int)} that provides more + * information about a composite candidate, which may be useful when generating or validating + * RSA moduli. + * + * @param candidate + * the {@link BigInteger} instance to test for primality. + * @param random + * the source of randomness to use to choose bases. + * @param iterations + * the number of randomly-chosen bases to perform the test for. + * @return an {@link MROutput} instance that can be further queried for details. + */ + public static MROutput EnhancedMRProbablePrimeTest(BigInteger candidate, SecureRandom random, int iterations) + { + CheckCandidate(candidate, "candidate"); + + if (random == null) + throw new ArgumentNullException("random"); + if (iterations < 1) + throw new ArgumentException("must be > 0", "iterations"); + + if (candidate.BitLength == 2) + return MROutput.ProbablyPrime(); + + if (!candidate.TestBit(0)) + return MROutput.ProvablyCompositeWithFactor(Two); + + BigInteger w = candidate; + BigInteger wSubOne = candidate.Subtract(One); + BigInteger wSubTwo = candidate.Subtract(Two); + + int a = wSubOne.GetLowestSetBit(); + BigInteger m = wSubOne.ShiftRight(a); + + for (int i = 0; i < iterations; ++i) + { + BigInteger b = BigIntegers.CreateRandomInRange(Two, wSubTwo, random); + BigInteger g = b.Gcd(w); + + if (g.CompareTo(One) > 0) + return MROutput.ProvablyCompositeWithFactor(g); + + BigInteger z = b.ModPow(m, w); + + if (z.Equals(One) || z.Equals(wSubOne)) + continue; + + bool primeToBase = false; + + BigInteger x = z; + for (int j = 1; j < a; ++j) + { + z = z.ModPow(Two, w); + + if (z.Equals(wSubOne)) + { + primeToBase = true; + break; + } + + if (z.Equals(One)) + break; + + x = z; + } + + if (!primeToBase) + { + if (!z.Equals(One)) + { + x = z; + z = z.ModPow(Two, w); + + if (!z.Equals(One)) + { + x = z; + } + } + + g = x.Subtract(One).Gcd(w); + + if (g.CompareTo(One) > 0) + return MROutput.ProvablyCompositeWithFactor(g); + + return MROutput.ProvablyCompositeNotPrimePower(); + } + } + + return MROutput.ProbablyPrime(); + } + + /** + * A fast check for small divisors, up to some implementation-specific limit. + * + * @param candidate + * the {@link BigInteger} instance to test for division by small factors. + * + * @return <code>true</code> if the candidate is found to have any small factors, + * <code>false</code> otherwise. + */ + public static bool HasAnySmallFactors(BigInteger candidate) + { + CheckCandidate(candidate, "candidate"); + + return ImplHasAnySmallFactors(candidate); + } + + /** + * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test + * + * Run several iterations of the Miller-Rabin algorithm with randomly-chosen bases. + * + * @param candidate + * the {@link BigInteger} instance to test for primality. + * @param random + * the source of randomness to use to choose bases. + * @param iterations + * the number of randomly-chosen bases to perform the test for. + * @return <code>false</code> if any witness to compositeness is found amongst the chosen bases + * (so <code>candidate</code> is definitely NOT prime), or else <code>true</code> + * (indicating primality with some probability dependent on the number of iterations + * that were performed). + */ + public static bool IsMRProbablePrime(BigInteger candidate, SecureRandom random, int iterations) + { + CheckCandidate(candidate, "candidate"); + + if (random == null) + throw new ArgumentException("cannot be null", "random"); + if (iterations < 1) + throw new ArgumentException("must be > 0", "iterations"); + + if (candidate.BitLength == 2) + return true; + if (!candidate.TestBit(0)) + return false; + + BigInteger w = candidate; + BigInteger wSubOne = candidate.Subtract(One); + BigInteger wSubTwo = candidate.Subtract(Two); + + int a = wSubOne.GetLowestSetBit(); + BigInteger m = wSubOne.ShiftRight(a); + + for (int i = 0; i < iterations; ++i) + { + BigInteger b = BigIntegers.CreateRandomInRange(Two, wSubTwo, random); + + if (!ImplMRProbablePrimeToBase(w, wSubOne, m, a, b)) + return false; + } + + return true; + } + + /** + * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test (to a fixed base). + * + * Run a single iteration of the Miller-Rabin algorithm against the specified base. + * + * @param candidate + * the {@link BigInteger} instance to test for primality. + * @param baseValue + * the base value to use for this iteration. + * @return <code>false</code> if the specified base is a witness to compositeness (so + * <code>candidate</code> is definitely NOT prime), or else <code>true</code>. + */ + public static bool IsMRProbablePrimeToBase(BigInteger candidate, BigInteger baseValue) + { + CheckCandidate(candidate, "candidate"); + CheckCandidate(baseValue, "baseValue"); + + if (baseValue.CompareTo(candidate.Subtract(One)) >= 0) + throw new ArgumentException("must be < ('candidate' - 1)", "baseValue"); + + if (candidate.BitLength == 2) + return true; + + BigInteger w = candidate; + BigInteger wSubOne = candidate.Subtract(One); + + int a = wSubOne.GetLowestSetBit(); + BigInteger m = wSubOne.ShiftRight(a); + + return ImplMRProbablePrimeToBase(w, wSubOne, m, a, baseValue); + } + + private static void CheckCandidate(BigInteger n, string name) + { + if (n == null || n.SignValue < 1 || n.BitLength < 2) + throw new ArgumentException("must be non-null and >= 2", name); + } + + private static bool ImplHasAnySmallFactors(BigInteger x) + { + /* + * Bundle trial divisors into ~32-bit moduli then use fast tests on the ~32-bit remainders. + */ + int m = 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23; + int r = x.Mod(BigInteger.ValueOf(m)).IntValue; + if ((r & 1) != 0 && (r % 3) != 0 && (r % 5) != 0 && (r % 7) != 0 && (r % 11) != 0 + && (r % 13) != 0 && (r % 17) != 0 && (r % 19) != 0 && (r % 23) != 0) + { + m = 29 * 31 * 37 * 41 * 43; + r = x.Mod(BigInteger.ValueOf(m)).IntValue; + if ((r % 29) != 0 && (r % 31) != 0 && (r % 37) != 0 && (r % 41) != 0 && (r % 43) != 0) + { + m = 47 * 53 * 59 * 61 * 67; + r = x.Mod(BigInteger.ValueOf(m)).IntValue; + if ((r % 47) != 0 && (r % 53) != 0 && (r % 59) != 0 && (r % 61) != 0 && (r % 67) != 0) + { + m = 71 * 73 * 79 * 83; + r = x.Mod(BigInteger.ValueOf(m)).IntValue; + if ((r % 71) != 0 && (r % 73) != 0 && (r % 79) != 0 && (r % 83) != 0) + { + m = 89 * 97 * 101 * 103; + r = x.Mod(BigInteger.ValueOf(m)).IntValue; + if ((r % 89) != 0 && (r % 97) != 0 && (r % 101) != 0 && (r % 103) != 0) + { + m = 107 * 109 * 113 * 127; + r = x.Mod(BigInteger.ValueOf(m)).IntValue; + if ((r % 107) != 0 && (r % 109) != 0 && (r % 113) != 0 && (r % 127) != 0) + { + return false; + } + } + } + } + } + } + return true; + } + + private static bool ImplMRProbablePrimeToBase(BigInteger w, BigInteger wSubOne, BigInteger m, int a, BigInteger b) + { + BigInteger z = b.ModPow(m, w); + + if (z.Equals(One) || z.Equals(wSubOne)) + return true; + + bool result = false; + + for (int j = 1; j < a; ++j) + { + z = z.ModPow(Two, w); + + if (z.Equals(wSubOne)) + { + result = true; + break; + } + + if (z.Equals(One)) + return false; + } + + return result; + } + + private static STOutput ImplSTRandomPrime(IDigest d, int length, byte[] primeSeed) + { + int dLen = d.GetDigestSize(); + + if (length < 33) + { + int primeGenCounter = 0; + + byte[] c0 = new byte[dLen]; + byte[] c1 = new byte[dLen]; + + for (;;) + { + Hash(d, primeSeed, c0, 0); + Inc(primeSeed, 1); + + Hash(d, primeSeed, c1, 0); + Inc(primeSeed, 1); + + uint c = Extract32(c0) ^ Extract32(c1); + c &= (uint.MaxValue >> (32 - length)); + c |= (1U << (length - 1)) | 1U; + + ++primeGenCounter; + + if (IsPrime32(c)) + { + return new STOutput(BigInteger.ValueOf((long)c), primeSeed, primeGenCounter); + } + + if (primeGenCounter > (4 * length)) + { + throw new InvalidOperationException("Too many iterations in Shawe-Taylor Random_Prime Routine"); + } + } + } + + STOutput rec = ImplSTRandomPrime(d, (length + 3)/2, primeSeed); + + { + BigInteger c0 = rec.Prime; + primeSeed = rec.PrimeSeed; + int primeGenCounter = rec.PrimeGenCounter; + + int outlen = 8 * dLen; + int iterations = (length - 1)/outlen; + + int oldCounter = primeGenCounter; + + BigInteger x = HashGen(d, primeSeed, iterations + 1); + x = x.Mod(One.ShiftLeft(length - 1)).SetBit(length - 1); + + BigInteger c0x2 = c0.ShiftLeft(1); + BigInteger tx2 = x.Subtract(One).Divide(c0x2).Add(One).ShiftLeft(1); + int dt = 0; + + BigInteger c = tx2.Multiply(c0).Add(One); + + /* + * TODO Since the candidate primes are generated by constant steps ('c0x2'), + * sieving could be used here in place of the 'HasAnySmallFactors' approach. + */ + for (;;) + { + if (c.BitLength > length) + { + tx2 = One.ShiftLeft(length - 1).Subtract(One).Divide(c0x2).Add(One).ShiftLeft(1); + c = tx2.Multiply(c0).Add(One); + } + + ++primeGenCounter; + + /* + * This is an optimization of the original algorithm, using trial division to screen out + * many non-primes quickly. + * + * NOTE: 'primeSeed' is still incremented as if we performed the full check! + */ + if (!ImplHasAnySmallFactors(c)) + { + BigInteger a = HashGen(d, primeSeed, iterations + 1); + a = a.Mod(c.Subtract(Three)).Add(Two); + + tx2 = tx2.Add(BigInteger.ValueOf(dt)); + dt = 0; + + BigInteger z = a.ModPow(tx2, c); + + if (c.Gcd(z.Subtract(One)).Equals(One) && z.ModPow(c0, c).Equals(One)) + { + return new STOutput(c, primeSeed, primeGenCounter); + } + } + else + { + Inc(primeSeed, iterations + 1); + } + + if (primeGenCounter >= ((4 * length) + oldCounter)) + { + throw new InvalidOperationException("Too many iterations in Shawe-Taylor Random_Prime Routine"); + } + + dt += 2; + c = c.Add(c0x2); + } + } + } + + private static uint Extract32(byte[] bs) + { + uint result = 0; + + int count = System.Math.Min(4, bs.Length); + for (int i = 0; i < count; ++i) + { + uint b = bs[bs.Length - (i + 1)]; + result |= (b << (8 * i)); + } + + return result; + } + + private static void Hash(IDigest d, byte[] input, byte[] output, int outPos) + { + d.BlockUpdate(input, 0, input.Length); + d.DoFinal(output, outPos); + } + + private static BigInteger HashGen(IDigest d, byte[] seed, int count) + { + int dLen = d.GetDigestSize(); + int pos = count * dLen; + byte[] buf = new byte[pos]; + for (int i = 0; i < count; ++i) + { + pos -= dLen; + Hash(d, seed, buf, pos); + Inc(seed, 1); + } + return new BigInteger(1, buf); + } + + private static void Inc(byte[] seed, int c) + { + int pos = seed.Length; + while (c > 0 && --pos >= 0) + { + c += seed[pos]; + seed[pos] = (byte)c; + c >>= 8; + } + } + + private static bool IsPrime32(uint x) + { + /* + * Use wheel factorization with 2, 3, 5 to select trial divisors. + */ + + if (x <= 5) + { + return x == 2 || x == 3 || x == 5; + } + + if ((x & 1) == 0 || (x % 3) == 0 || (x % 5) == 0) + { + return false; + } + + uint[] ds = new uint[]{ 1, 7, 11, 13, 17, 19, 23, 29 }; + uint b = 0; + for (int pos = 1; ; pos = 0) + { + /* + * Trial division by wheel-selected divisors + */ + while (pos < ds.Length) + { + uint d = b + ds[pos]; + if (x % d == 0) + { + return x < 30; + } + ++pos; + } + + b += 30; + + if ((b >> 16 != 0) || (b * b >= x)) + { + return true; + } + } + } + } +} diff --git a/crypto/src/math/ec/ECAlgorithms.cs b/crypto/src/math/ec/ECAlgorithms.cs
index 6519e81c6..5d60de40f 100644 --- a/crypto/src/math/ec/ECAlgorithms.cs +++ b/crypto/src/math/ec/ECAlgorithms.cs
@@ -10,14 +10,23 @@ namespace Org.BouncyCastle.Math.EC { public static bool IsF2mCurve(ECCurve c) { - IFiniteField field = c.Field; + return IsF2mField(c.Field); + } + + public static bool IsF2mField(IFiniteField field) + { return field.Dimension > 1 && field.Characteristic.Equals(BigInteger.Two) && field is IPolynomialExtensionField; } public static bool IsFpCurve(ECCurve c) { - return c.Field.Dimension == 1; + return IsFpField(c.Field); + } + + public static bool IsFpField(IFiniteField field) + { + return field.Dimension == 1; } public static ECPoint SumOfMultiplies(ECPoint[] ps, BigInteger[] ks) @@ -49,10 +58,10 @@ namespace Org.BouncyCastle.Math.EC GlvEndomorphism glvEndomorphism = c.GetEndomorphism() as GlvEndomorphism; if (glvEndomorphism != null) { - return ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism); + return ValidatePoint(ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism)); } - return ImplSumOfMultiplies(imported, ks); + return ValidatePoint(ImplSumOfMultiplies(imported, ks)); } public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b) @@ -61,22 +70,22 @@ namespace Org.BouncyCastle.Math.EC Q = ImportPoint(cp, Q); // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick - if (cp is F2mCurve) { - F2mCurve f2mCurve = (F2mCurve) cp; - if (f2mCurve.IsKoblitz) + AbstractF2mCurve f2mCurve = cp as AbstractF2mCurve; + if (f2mCurve != null && f2mCurve.IsKoblitz) { - return P.Multiply(a).Add(Q.Multiply(b)); + return ValidatePoint(P.Multiply(a).Add(Q.Multiply(b))); } } GlvEndomorphism glvEndomorphism = cp.GetEndomorphism() as GlvEndomorphism; if (glvEndomorphism != null) { - return ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism); + return ValidatePoint( + ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism)); } - return ImplShamirsTrickWNaf(P, a, Q, b); + return ValidatePoint(ImplShamirsTrickWNaf(P, a, Q, b)); } /* @@ -102,7 +111,7 @@ namespace Org.BouncyCastle.Math.EC ECCurve cp = P.Curve; Q = ImportPoint(cp, Q); - return ImplShamirsTrickJsf(P, k, Q, l); + return ValidatePoint(ImplShamirsTrickJsf(P, k, Q, l)); } public static ECPoint ImportPoint(ECCurve c, ECPoint p) @@ -116,6 +125,11 @@ namespace Org.BouncyCastle.Math.EC public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len) { + MontgomeryTrick(zs, off, len, null); + } + + public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len, ECFieldElement scale) + { /* * Uses the "Montgomery Trick" to invert many field elements, with only a single actual * field inversion. See e.g. the paper: @@ -132,7 +146,14 @@ namespace Org.BouncyCastle.Math.EC c[i] = c[i - 1].Multiply(zs[off + i]); } - ECFieldElement u = c[--i].Invert(); + --i; + + if (scale != null) + { + c[i] = c[i].Multiply(scale); + } + + ECFieldElement u = c[i].Invert(); while (i > 0) { @@ -145,6 +166,47 @@ namespace Org.BouncyCastle.Math.EC zs[off] = u; } + /** + * Simple shift-and-add multiplication. Serves as reference implementation + * to verify (possibly faster) implementations, and for very small scalars. + * + * @param p + * The point to multiply. + * @param k + * The multiplier. + * @return The result of the point multiplication <code>kP</code>. + */ + public static ECPoint ReferenceMultiply(ECPoint p, BigInteger k) + { + BigInteger x = k.Abs(); + ECPoint q = p.Curve.Infinity; + int t = x.BitLength; + if (t > 0) + { + if (x.TestBit(0)) + { + q = p; + } + for (int i = 1; i < t; i++) + { + p = p.Twice(); + if (x.TestBit(i)) + { + q = q.Add(p); + } + } + } + return k.SignValue < 0 ? q.Negate() : q; + } + + public static ECPoint ValidatePoint(ECPoint p) + { + if (!p.IsValid()) + throw new ArgumentException("Invalid point", "p"); + + return p; + } + internal static ECPoint ImplShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { ECCurve curve = P.Curve; @@ -386,7 +448,7 @@ namespace Org.BouncyCastle.Math.EC { int n = System.Math.Abs(wi); WNafPreCompInfo info = infos[j]; - ECPoint[] table = (wi < 0 == negs[j]) ? info.PreCompNeg : info.PreComp; + ECPoint[] table = (wi < 0 == negs[j]) ? info.PreComp : info.PreCompNeg; r = r.Add(table[n >> 1]); } } diff --git a/crypto/src/math/ec/ECCurve.cs b/crypto/src/math/ec/ECCurve.cs
index 9c16375e6..40b46ce72 100644 --- a/crypto/src/math/ec/ECCurve.cs +++ b/crypto/src/math/ec/ECCurve.cs
@@ -102,6 +102,27 @@ namespace Org.BouncyCastle.Math.EC return new Config(this, this.m_coord, this.m_endomorphism, this.m_multiplier); } + public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y) + { + ECPoint p = CreatePoint(x, y); + if (!p.IsValid()) + { + throw new ArgumentException("Invalid point coordinates"); + } + return p; + } + + [Obsolete("Per-point compression property will be removed")] + public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y, bool withCompression) + { + ECPoint p = CreatePoint(x, y, withCompression); + if (!p.IsValid()) + { + throw new ArgumentException("Invalid point coordinates"); + } + return p; + } + public virtual ECPoint CreatePoint(BigInteger x, BigInteger y) { return CreatePoint(x, y, false); @@ -185,7 +206,7 @@ namespace Org.BouncyCastle.Math.EC // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates. p = p.Normalize(); - return CreatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger(), p.IsCompressed); + return ValidatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger(), p.IsCompressed); } /** @@ -200,26 +221,56 @@ namespace Org.BouncyCastle.Math.EC */ public virtual void NormalizeAll(ECPoint[] points) { - CheckPoints(points); + NormalizeAll(points, 0, points.Length, null); + } + + /** + * Normalization ensures that any projective coordinate is 1, and therefore that the x, y + * coordinates reflect those of the equivalent point in an affine coordinate system. Where more + * than one point is to be normalized, this method will generally be more efficient than + * normalizing each point separately. An (optional) z-scaling factor can be applied; effectively + * each z coordinate is scaled by this value prior to normalization (but only one + * actual multiplication is needed). + * + * @param points + * An array of points that will be updated in place with their normalized versions, + * where necessary + * @param off + * The start of the range of points to normalize + * @param len + * The length of the range of points to normalize + * @param iso + * The (optional) z-scaling factor - can be null + */ + public virtual void NormalizeAll(ECPoint[] points, int off, int len, ECFieldElement iso) + { + CheckPoints(points, off, len); - if (this.CoordinateSystem == ECCurve.COORD_AFFINE) + switch (this.CoordinateSystem) { - return; + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + { + if (iso != null) + throw new ArgumentException("not valid for affine coordinates", "iso"); + + return; + } } /* * Figure out which of the points actually need to be normalized */ - ECFieldElement[] zs = new ECFieldElement[points.Length]; - int[] indices = new int[points.Length]; + ECFieldElement[] zs = new ECFieldElement[len]; + int[] indices = new int[len]; int count = 0; - for (int i = 0; i < points.Length; ++i) + for (int i = 0; i < len; ++i) { - ECPoint p = points[i]; - if (null != p && !p.IsNormalized()) + ECPoint p = points[off + i]; + if (null != p && (iso != null || !p.IsNormalized())) { zs[count] = p.GetZCoord(0); - indices[count++] = i; + indices[count++] = off + i; } } @@ -228,7 +279,7 @@ namespace Org.BouncyCastle.Math.EC return; } - ECAlgorithms.MontgomeryTrick(zs, 0, count); + ECAlgorithms.MontgomeryTrick(zs, 0, count, iso); for (int j = 0; j < count; ++j) { @@ -277,12 +328,19 @@ namespace Org.BouncyCastle.Math.EC protected virtual void CheckPoints(ECPoint[] points) { + CheckPoints(points, 0, points.Length); + } + + protected virtual void CheckPoints(ECPoint[] points, int off, int len) + { if (points == null) throw new ArgumentNullException("points"); + if (off < 0 || len < 0 || (off > (points.Length - len))) + throw new ArgumentException("invalid range specified", "points"); - for (int i = 0; i < points.Length; ++i) + for (int i = 0; i < len; ++i) { - ECPoint point = points[i]; + ECPoint point = points[off + i]; if (null != point && this != point.Curve) throw new ArgumentException("entries must be null or on this curve", "points"); } @@ -344,7 +402,8 @@ namespace Org.BouncyCastle.Math.EC ECPoint p = null; int expectedLength = (FieldSize + 7) / 8; - switch (encoded[0]) + byte type = encoded[0]; + switch (type) { case 0x00: // infinity { @@ -361,10 +420,13 @@ namespace Org.BouncyCastle.Math.EC if (encoded.Length != (expectedLength + 1)) throw new ArgumentException("Incorrect length for compressed encoding", "encoded"); - int yTilde = encoded[0] & 1; + int yTilde = type & 1; BigInteger X = new BigInteger(1, encoded, 1, expectedLength); p = DecompressPoint(yTilde, X); + if (!p.SatisfiesCofactor()) + throw new ArgumentException("Invalid point"); + break; } @@ -376,7 +438,7 @@ namespace Org.BouncyCastle.Math.EC BigInteger X = new BigInteger(1, encoded, 1, expectedLength); BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); - p = CreatePoint(X, Y); + p = ValidatePoint(X, Y); break; } @@ -389,26 +451,59 @@ namespace Org.BouncyCastle.Math.EC BigInteger X = new BigInteger(1, encoded, 1, expectedLength); BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); - if (Y.TestBit(0) != (encoded[0] == 0x07)) + if (Y.TestBit(0) != (type == 0x07)) throw new ArgumentException("Inconsistent Y coordinate in hybrid encoding", "encoded"); - p = CreatePoint(X, Y); + p = ValidatePoint(X, Y); break; } default: - throw new FormatException("Invalid point encoding " + encoded[0]); + throw new FormatException("Invalid point encoding " + type); } + if (type != 0x00 && p.IsInfinity) + throw new ArgumentException("Invalid infinity encoding", "encoded"); + return p; } } + public abstract class AbstractFpCurve + : ECCurve + { + protected AbstractFpCurve(BigInteger q) + : base(FiniteFields.GetPrimeField(q)) + { + } + + protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) + { + ECFieldElement x = FromBigInteger(X1); + ECFieldElement rhs = x.Square().Add(A).Multiply(x).Add(B); + ECFieldElement y = rhs.Sqrt(); + + /* + * If y is not a square, then we haven't got a point on the curve + */ + if (y == null) + throw new ArgumentException("Invalid point compression"); + + if (y.TestBitZero() != (yTilde == 1)) + { + // Use the other root + y = y.Negate(); + } + + return CreateRawPoint(x, y, true); + } + } + /** * Elliptic curve over Fp */ public class FpCurve - : ECCurve + : AbstractFpCurve { private const int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED; @@ -421,7 +516,7 @@ namespace Org.BouncyCastle.Math.EC } public FpCurve(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor) - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_q = q; this.m_r = FpFieldElement.CalculateResidue(q); @@ -440,7 +535,7 @@ namespace Org.BouncyCastle.Math.EC } protected FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor) - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_q = q; this.m_r = r; @@ -523,37 +618,22 @@ namespace Org.BouncyCastle.Math.EC return base.ImportPoint(p); } + } - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) + public abstract class AbstractF2mCurve + : ECCurve + { + public static BigInteger Inverse(int m, int[] ks, BigInteger x) { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(m_a).Multiply(x).Add(m_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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new FpPoint(this, x, beta, true); + return new LongArray(x).ModInverse(m, ks).ToBigInteger(); } - } - /** - * Elliptic curves over F2m. The Weierstrass equation is given by - * <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>. - */ - public class F2mCurve : ECCurve - { - private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + /** + * The auxiliary values <code>s<sub>0</sub></code> and + * <code>s<sub>1</sub></code> used for partial modular reduction for + * Koblitz curves. + */ + private BigInteger[] si = null; private static IFiniteField BuildField(int m, int k1, int k2, int k3) { @@ -585,6 +665,166 @@ namespace Org.BouncyCastle.Math.EC return FiniteFields.GetBinaryExtensionField(new int[]{ 0, k1, k2, k3, m }); } + protected AbstractF2mCurve(int m, int k1, int k2, int k3) + : base(BuildField(m, k1, k2, k3)) + { + } + + [Obsolete("Per-point compression property will be removed")] + public override ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression) + { + ECFieldElement X = FromBigInteger(x), Y = FromBigInteger(y); + + switch (this.CoordinateSystem) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + if (X.IsZero) + { + if (!Y.Square().Equals(B)) + throw new ArgumentException(); + } + else + { + // Y becomes Lambda (X + Y/X) here + Y = Y.Divide(X).Add(X); + } + break; + } + default: + { + break; + } + } + + return CreateRawPoint(X, Y, withCompression); + } + + protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) + { + ECFieldElement xp = FromBigInteger(X1), yp = null; + if (xp.IsZero) + { + yp = B.Sqrt(); + } + else + { + ECFieldElement beta = xp.Square().Invert().Multiply(B).Add(A).Add(xp); + ECFieldElement z = SolveQuadradicEquation(beta); + + if (z != null) + { + if (z.TestBitZero() != (yTilde == 1)) + { + z = z.AddOne(); + } + + switch (this.CoordinateSystem) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + yp = z.Add(xp); + break; + } + default: + { + yp = z.Multiply(xp); + break; + } + } + } + } + + if (yp == null) + throw new ArgumentException("Invalid point compression"); + + return CreateRawPoint(xp, yp, true); + } + + /** + * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62 + * D.1.6) The other solution is <code>z + 1</code>. + * + * @param beta + * The value to solve the qradratic equation for. + * @return the solution for <code>z<sup>2</sup> + z = beta</code> or + * <code>null</code> if no solution exists. + */ + private ECFieldElement SolveQuadradicEquation(ECFieldElement beta) + { + if (beta.IsZero) + return beta; + + ECFieldElement gamma, z, zeroElement = FromBigInteger(BigInteger.Zero); + + int m = FieldSize; + Random rand = new Random(); + do + { + ECFieldElement t = FromBigInteger(new BigInteger(m, rand)); + z = zeroElement; + ECFieldElement w = beta; + for (int i = 1; i < m; i++) + { + ECFieldElement w2 = w.Square(); + z = z.Square().Add(w2.Multiply(t)); + w = w2.Add(beta); + } + if (!w.IsZero) + { + return null; + } + gamma = z.Square().Add(z); + } + while (gamma.IsZero); + + return z; + } + + /** + * @return the auxiliary values <code>s<sub>0</sub></code> and + * <code>s<sub>1</sub></code> used for partial modular reduction for + * Koblitz curves. + */ + internal virtual BigInteger[] GetSi() + { + if (si == null) + { + lock (this) + { + if (si == null) + { + si = Tnaf.GetSi(this); + } + } + } + return si; + } + + /** + * Returns true if this is a Koblitz curve (ABC curve). + * @return true if this is a Koblitz curve (ABC curve), false otherwise + */ + public virtual bool IsKoblitz + { + get + { + return m_order != null && m_cofactor != null && m_b.IsOne && (m_a.IsZero || m_a.IsOne); + } + } + } + + /** + * Elliptic curves over F2m. The Weierstrass equation is given by + * <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>. + */ + public class F2mCurve + : AbstractF2mCurve + { + private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + /** * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>. */ @@ -622,19 +862,6 @@ namespace Org.BouncyCastle.Math.EC protected readonly F2mPoint m_infinity; /** - * The parameter <code>&#956;</code> of the elliptic curve if this is - * a Koblitz curve. - */ - private sbyte mu = 0; - - /** - * The auxiliary values <code>s<sub>0</sub></code> and - * <code>s<sub>1</sub></code> used for partial modular reduction for - * Koblitz curves. - */ - private BigInteger[] si = null; - - /** * Constructor for Trinomial Polynomial Basis (TPB). * @param m The exponent <code>m</code> of * <code>F<sub>2<sup>m</sup></sub></code>. @@ -748,7 +975,7 @@ namespace Org.BouncyCastle.Math.EC BigInteger b, BigInteger order, BigInteger cofactor) - : base(BuildField(m, k1, k2, k3)) + : base(m, k1, k2, k3) { this.m = m; this.k1 = k1; @@ -781,7 +1008,7 @@ namespace Org.BouncyCastle.Math.EC } protected F2mCurve(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor) - : base(BuildField(m, k1, k2, k3)) + : base(m, k1, k2, k3) { this.m = m; this.k1 = k1; @@ -834,37 +1061,6 @@ namespace Org.BouncyCastle.Math.EC return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, x); } - [Obsolete("Per-point compression property will be removed")] - public override ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression) - { - ECFieldElement X = FromBigInteger(x), Y = FromBigInteger(y); - - switch (this.CoordinateSystem) - { - case COORD_LAMBDA_AFFINE: - case COORD_LAMBDA_PROJECTIVE: - { - if (X.IsZero) - { - if (!Y.Square().Equals(B)) - throw new ArgumentException(); - } - else - { - // Y becomes Lambda (X + Y/X) here - Y = Y.Divide(X).Add(X); - } - break; - } - default: - { - break; - } - } - - return CreateRawPoint(X, Y, withCompression); - } - protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) { return new F2mPoint(this, x, y, withCompression); @@ -880,143 +1076,6 @@ namespace Org.BouncyCastle.Math.EC get { return m_infinity; } } - /** - * Returns true if this is a Koblitz curve (ABC curve). - * @return true if this is a Koblitz curve (ABC curve), false otherwise - */ - public virtual bool IsKoblitz - { - get - { - return m_order != null && m_cofactor != null && m_b.IsOne && (m_a.IsZero || m_a.IsOne); - } - } - - /** - * Returns the parameter <code>&#956;</code> of the elliptic curve. - * @return <code>&#956;</code> of the elliptic curve. - * @throws ArgumentException if the given ECCurve is not a - * Koblitz curve. - */ - internal virtual sbyte GetMu() - { - if (mu == 0) - { - lock (this) - { - if (mu == 0) - { - mu = Tnaf.GetMu(this); - } - } - } - - return mu; - } - - /** - * @return the auxiliary values <code>s<sub>0</sub></code> and - * <code>s<sub>1</sub></code> used for partial modular reduction for - * Koblitz curves. - */ - internal virtual BigInteger[] GetSi() - { - if (si == null) - { - lock (this) - { - if (si == null) - { - si = Tnaf.GetSi(this); - } - } - } - return si; - } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement xp = FromBigInteger(X1), yp; - if (xp.IsZero) - { - yp = m_b.Sqrt(); - } - else - { - ECFieldElement beta = xp.Square().Invert().Multiply(B).Add(A).Add(xp); - ECFieldElement z = SolveQuadradicEquation(beta); - - if (z == null) - throw new ArithmeticException("Invalid point compression"); - - if (z.TestBitZero() != (yTilde == 1)) - { - z = z.AddOne(); - } - - switch (this.CoordinateSystem) - { - case COORD_LAMBDA_AFFINE: - case COORD_LAMBDA_PROJECTIVE: - { - yp = z.Add(xp); - break; - } - default: - { - yp = z.Multiply(xp); - break; - } - } - } - - return new F2mPoint(this, xp, yp, true); - } - - /** - * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62 - * D.1.6) The other solution is <code>z + 1</code>. - * - * @param beta - * The value to solve the qradratic equation for. - * @return the solution for <code>z<sup>2</sup> + z = beta</code> or - * <code>null</code> if no solution exists. - */ - private ECFieldElement SolveQuadradicEquation(ECFieldElement beta) - { - if (beta.IsZero) - { - return beta; - } - - ECFieldElement zeroElement = FromBigInteger(BigInteger.Zero); - - ECFieldElement z = null; - ECFieldElement gamma = null; - - Random rand = new Random(); - do - { - ECFieldElement t = FromBigInteger(new BigInteger(m, rand)); - z = zeroElement; - 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.IsZero) - { - return null; - } - gamma = z.Square().Add(z); - } - while (gamma.IsZero); - - return z; - } - public int M { get { return m; } diff --git a/crypto/src/math/ec/ECFieldElement.cs b/crypto/src/math/ec/ECFieldElement.cs
index e589fc737..4d4fb3e4d 100644 --- a/crypto/src/math/ec/ECFieldElement.cs +++ b/crypto/src/math/ec/ECFieldElement.cs
@@ -1,6 +1,7 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC @@ -55,6 +56,16 @@ namespace Org.BouncyCastle.Math.EC return Square().Add(x.Multiply(y)); } + public virtual ECFieldElement SquarePow(int pow) + { + ECFieldElement r = this; + for (int i = 0; i < pow; ++i) + { + r = r.Square(); + } + return r; + } + public virtual bool TestBitZero() { return ToBigInteger().TestBit(0); @@ -593,6 +604,9 @@ namespace Org.BouncyCastle.Math.EC int k3, BigInteger x) { + if (x == null || x.SignValue < 0 || x.BitLength > m) + throw new ArgumentException("value invalid in F2m field element", "x"); + if ((k2 == 0) && (k3 == 0)) { this.representation = Tpb; @@ -812,6 +826,11 @@ namespace Org.BouncyCastle.Math.EC return new F2mFieldElement(m, ks, aa); } + public override ECFieldElement SquarePow(int pow) + { + return pow < 1 ? this : new F2mFieldElement(m, ks, x.ModSquareN(pow, m, ks)); + } + public override ECFieldElement Invert() { return new F2mFieldElement(this.m, this.ks, this.x.ModInverse(m, ks)); @@ -819,14 +838,7 @@ namespace Org.BouncyCastle.Math.EC public override ECFieldElement Sqrt() { - LongArray x1 = this.x; - if (x1.IsOne() || x1.IsZero()) - { - return this; - } - - LongArray x2 = x1.ModSquareN(m - 1, m, ks); - return new F2mFieldElement(m, ks, x2); + return (x.IsZero() || x.IsOne()) ? this : SquarePow(m - 1); } /** diff --git a/crypto/src/math/ec/ECPoint.cs b/crypto/src/math/ec/ECPoint.cs
index 0430a6110..a5ba515c5 100644 --- a/crypto/src/math/ec/ECPoint.cs +++ b/crypto/src/math/ec/ECPoint.cs
@@ -67,6 +67,14 @@ namespace Org.BouncyCastle.Math.EC this.m_withCompression = withCompression; } + protected internal bool SatisfiesCofactor() + { + BigInteger h = Curve.Cofactor; + return h == null || h.Equals(BigInteger.One) || !ECAlgorithms.ReferenceMultiply(this, h).IsInfinity; + } + + protected abstract bool SatisfiesCurveEquation(); + public ECPoint GetDetachedPoint() { return Normalize().Detach(); @@ -289,6 +297,26 @@ namespace Org.BouncyCastle.Math.EC get { return m_withCompression; } } + public bool IsValid() + { + if (IsInfinity) + return true; + + // TODO Sanity-check the field elements + + ECCurve curve = Curve; + if (curve != null) + { + if (!SatisfiesCurveEquation()) + return false; + + if (!SatisfiesCofactor()) + return false; + } + + return true; + } + public virtual ECPoint ScaleX(ECFieldElement scale) { return IsInfinity @@ -497,14 +525,84 @@ namespace Org.BouncyCastle.Math.EC } } + public abstract class AbstractFpPoint + : ECPointBase + { + protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected internal override bool CompressionYTilde + { + get { return this.AffineYCoord.TestBitZero(); } + } + + protected override bool SatisfiesCurveEquation() + { + ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = Curve.A, B = Curve.B; + ECFieldElement lhs = Y.Square(); + + switch (CurveCoordinateSystem) + { + case ECCurve.COORD_AFFINE: + break; + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z = this.RawZCoords[0]; + if (!Z.IsOne) + { + ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2); + lhs = lhs.Multiply(Z); + A = A.Multiply(Z2); + B = B.Multiply(Z3); + } + break; + } + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + ECFieldElement Z = this.RawZCoords[0]; + if (!Z.IsOne) + { + ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square(), Z6 = Z2.Multiply(Z4); + A = A.Multiply(Z4); + B = B.Multiply(Z6); + } + break; + } + default: + throw new InvalidOperationException("unsupported coordinate system"); + } + + ECFieldElement rhs = X.Square().Add(A).Multiply(X).Add(B); + return lhs.Equals(rhs); + } + + public override ECPoint Subtract(ECPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return Add(b.Negate()); + } + } + /** * Elliptic curve points over Fp */ public class FpPoint - : ECPointBase + : AbstractFpPoint { /** - * Create a point which encodes with point compression. + * Create a point which encodes without point compression. * * @param curve the curve to use * @param x affine x co-ordinate @@ -516,7 +614,7 @@ namespace Org.BouncyCastle.Math.EC } /** - * Create a point that encodes with or without point compresion. + * Create a point that encodes with or without point compression. * * @param curve the curve to use * @param x affine x co-ordinate @@ -540,11 +638,6 @@ namespace Org.BouncyCastle.Math.EC return new FpPoint(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECFieldElement GetZCoord(int index) { if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.CurveCoordinateSystem) @@ -1135,16 +1228,6 @@ namespace Org.BouncyCastle.Math.EC return a.Add(b).Square().Subtract(aSquared).Subtract(bSquared); } - public override ECPoint Subtract( - ECPoint b) - { - if (b.IsInfinity) - return this; - - // Add -b - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) @@ -1217,11 +1300,229 @@ namespace Org.BouncyCastle.Math.EC } } + public abstract class AbstractF2mPoint + : ECPointBase + { + protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override bool SatisfiesCurveEquation() + { + ECCurve curve = Curve; + ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = curve.A, B = curve.B; + ECFieldElement lhs, rhs; + + int coord = curve.CoordinateSystem; + if (coord == ECCurve.COORD_LAMBDA_PROJECTIVE) + { + ECFieldElement Z = this.RawZCoords[0]; + bool ZIsOne = Z.IsOne; + + if (X.IsZero) + { + // NOTE: For x == 0, we expect the affine-y instead of the lambda-y + lhs = Y.Square(); + rhs = B; + if (!ZIsOne) + { + ECFieldElement Z2 = Z.Square(); + rhs = rhs.Multiply(Z2); + } + } + else + { + ECFieldElement L = Y, X2 = X.Square(); + if (ZIsOne) + { + lhs = L.Square().Add(L).Add(A); + rhs = X2.Square().Add(B); + } + else + { + ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square(); + lhs = L.Add(Z).MultiplyPlusProduct(L, A, Z2); + // TODO If sqrt(b) is precomputed this can be simplified to a single square + rhs = X2.SquarePlusProduct(B, Z4); + } + lhs = lhs.Multiply(X2); + } + } + else + { + lhs = Y.Add(X).Multiply(Y); + + switch (coord) + { + case ECCurve.COORD_AFFINE: + break; + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z = this.RawZCoords[0]; + if (!Z.IsOne) + { + ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2); + lhs = lhs.Multiply(Z); + A = A.Multiply(Z); + B = B.Multiply(Z3); + } + break; + } + default: + throw new InvalidOperationException("unsupported coordinate system"); + } + + rhs = X.Add(A).Multiply(X.Square()).Add(B); + } + + return lhs.Equals(rhs); + } + + public override ECPoint ScaleX(ECFieldElement scale) + { + if (this.IsInfinity) + return this; + + switch (CurveCoordinateSystem) + { + case ECCurve.COORD_LAMBDA_AFFINE: + { + // Y is actually Lambda (X + Y/X) here + ECFieldElement X = RawXCoord, L = RawYCoord; + + ECFieldElement X2 = X.Multiply(scale); + ECFieldElement L2 = L.Add(X).Divide(scale).Add(X2); + + return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // Y is actually Lambda (X + Y/X) here + ECFieldElement X = RawXCoord, L = RawYCoord, Z = RawZCoords[0]; + + // We scale the Z coordinate also, to avoid an inversion + ECFieldElement X2 = X.Multiply(scale.Square()); + ECFieldElement L2 = L.Add(X).Add(X2); + ECFieldElement Z2 = Z.Multiply(scale); + + return Curve.CreateRawPoint(X, L2, new ECFieldElement[] { Z2 }, IsCompressed); + } + default: + { + return base.ScaleX(scale); + } + } + } + + public override ECPoint ScaleY(ECFieldElement scale) + { + if (this.IsInfinity) + return this; + + switch (CurveCoordinateSystem) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + // Y is actually Lambda (X + Y/X) here + ECFieldElement L2 = L.Add(X).Multiply(scale).Add(X); + + return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed); + } + default: + { + return base.ScaleY(scale); + } + } + } + + public override ECPoint Subtract(ECPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return Add(b.Negate()); + } + + public virtual AbstractF2mPoint Tau() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + ECFieldElement X1 = this.RawXCoord; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + return (AbstractF2mPoint)curve.CreateRawPoint(X1.Square(), Y1.Square(), IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + return (AbstractF2mPoint)curve.CreateRawPoint(X1.Square(), Y1.Square(), + new ECFieldElement[] { Z1.Square() }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + + public virtual AbstractF2mPoint TauPow(int pow) + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + ECFieldElement X1 = this.RawXCoord; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + return (AbstractF2mPoint)curve.CreateRawPoint(X1.SquarePow(pow), Y1.SquarePow(pow), IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + return (AbstractF2mPoint)curve.CreateRawPoint(X1.SquarePow(pow), Y1.SquarePow(pow), + new ECFieldElement[] { Z1.SquarePow(pow) }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + } + /** * Elliptic curve points over F2m */ public class F2mPoint - : ECPointBase + : AbstractF2mPoint { /** * @param curve base curve @@ -1323,66 +1624,6 @@ namespace Org.BouncyCastle.Math.EC } } - public override ECPoint ScaleX(ECFieldElement scale) - { - if (this.IsInfinity) - return this; - - switch (CurveCoordinateSystem) - { - case ECCurve.COORD_LAMBDA_AFFINE: - { - // Y is actually Lambda (X + Y/X) here - ECFieldElement X = RawXCoord, L = RawYCoord; - - ECFieldElement X2 = X.Multiply(scale); - ECFieldElement L2 = L.Add(X).Divide(scale).Add(X2); - - return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed); - } - case ECCurve.COORD_LAMBDA_PROJECTIVE: - { - // Y is actually Lambda (X + Y/X) here - ECFieldElement X = RawXCoord, L = RawYCoord, Z = RawZCoords[0]; - - // We scale the Z coordinate also, to avoid an inversion - ECFieldElement X2 = X.Multiply(scale.Square()); - ECFieldElement L2 = L.Add(X).Add(X2); - ECFieldElement Z2 = Z.Multiply(scale); - - return Curve.CreateRawPoint(X, L2, new ECFieldElement[] { Z2 }, IsCompressed); - } - default: - { - return base.ScaleX(scale); - } - } - } - - public override ECPoint ScaleY(ECFieldElement scale) - { - if (this.IsInfinity) - return this; - - switch (CurveCoordinateSystem) - { - case ECCurve.COORD_LAMBDA_AFFINE: - case ECCurve.COORD_LAMBDA_PROJECTIVE: - { - ECFieldElement X = RawXCoord, L = RawYCoord; - - // Y is actually Lambda (X + Y/X) here - ECFieldElement L2 = L.Add(X).Multiply(scale).Add(X); - - return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed); - } - default: - { - return base.ScaleY(scale); - } - } - } - protected internal override bool CompressionYTilde { get @@ -1411,44 +1652,8 @@ namespace Org.BouncyCastle.Math.EC } } - /** - * Check, if two <code>ECPoint</code>s can be added or subtracted. - * @param a The first <code>ECPoint</code> to check. - * @param b The second <code>ECPoint</code> to check. - * @throws IllegalArgumentException if <code>a</code> and <code>b</code> - * 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 <code>ECPoints.F2m</code> to <code>this</code> 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 <code>ECPoints.F2m</code> to add to - * <code>this</code>. - * @return <code>this + b</code> - */ - internal F2mPoint AddSimple(F2mPoint b) - { if (this.IsInfinity) return b; if (b.IsInfinity) @@ -1472,10 +1677,10 @@ namespace Org.BouncyCastle.Math.EC { if (dy.IsZero) { - return (F2mPoint)Twice(); + return Twice(); } - return (F2mPoint)curve.Infinity; + return curve.Infinity; } ECFieldElement L = dy.Divide(dx); @@ -1513,10 +1718,10 @@ namespace Org.BouncyCastle.Math.EC { if (U.IsZero) { - return (F2mPoint)Twice(); + return Twice(); } - return (F2mPoint)curve.Infinity; + return curve.Infinity; } ECFieldElement VSq = V.Square(); @@ -1537,9 +1742,9 @@ namespace Org.BouncyCastle.Math.EC if (X1.IsZero) { if (X2.IsZero) - return (F2mPoint)curve.Infinity; + return curve.Infinity; - return b.AddSimple(this); + return b.Add(this); } ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; @@ -1568,10 +1773,10 @@ namespace Org.BouncyCastle.Math.EC { if (A.IsZero) { - return (F2mPoint)Twice(); + return Twice(); } - return (F2mPoint)curve.Infinity; + return curve.Infinity; } ECFieldElement X3, L3, Z3; @@ -1633,68 +1838,6 @@ namespace Org.BouncyCastle.Math.EC } /* (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 <code>ECPoints.F2m</code> from <code>this</code> - * 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 <code>ECPoints.F2m</code> to subtract from - * <code>this</code>. - * @return <code>this - b</code> - */ - internal F2mPoint SubtractSimple( - F2mPoint b) - { - if (b.IsInfinity) - return this; - - // Add -b - return AddSimple((F2mPoint) b.Negate()); - } - - public virtual F2mPoint Tau() - { - if (this.IsInfinity) - { - return this; - } - - ECCurve curve = this.Curve; - int coord = curve.CoordinateSystem; - - ECFieldElement X1 = this.RawXCoord; - - switch (coord) - { - case ECCurve.COORD_AFFINE: - case ECCurve.COORD_LAMBDA_AFFINE: - { - ECFieldElement Y1 = this.RawYCoord; - return new F2mPoint(curve, X1.Square(), Y1.Square(), IsCompressed); - } - case ECCurve.COORD_HOMOGENEOUS: - case ECCurve.COORD_LAMBDA_PROJECTIVE: - { - ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0]; - return new F2mPoint(curve, X1.Square(), Y1.Square(), new ECFieldElement[] { Z1.Square() }, IsCompressed); - } - default: - { - throw new InvalidOperationException("unsupported coordinate system"); - } - } - } - - /* (non-Javadoc) * @see Org.BouncyCastle.Math.EC.ECPoint#twice() */ public override ECPoint Twice() diff --git a/crypto/src/math/ec/LongArray.cs b/crypto/src/math/ec/LongArray.cs
index c4e3dacbc..84462e0ea 100644 --- a/crypto/src/math/ec/LongArray.cs +++ b/crypto/src/math/ec/LongArray.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Math.EC * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits. * In a binary field, this operation is the same as squaring an 8 bit number. */ - private static readonly int[] INTERLEAVE2_TABLE = new int[] + private static readonly ushort[] INTERLEAVE2_TABLE = new ushort[] { 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, diff --git a/crypto/src/math/ec/abc/Tnaf.cs b/crypto/src/math/ec/abc/Tnaf.cs
index 9f16886f5..b6e792aa4 100644 --- a/crypto/src/math/ec/abc/Tnaf.cs +++ b/crypto/src/math/ec/abc/Tnaf.cs
@@ -384,11 +384,11 @@ namespace Org.BouncyCastle.Math.EC.Abc /** * Applies the operation <code>&#964;()</code> to an - * <code>F2mPoint</code>. - * @param p The F2mPoint to which <code>&#964;()</code> is applied. + * <code>AbstractF2mPoint</code>. + * @param p The AbstractF2mPoint to which <code>&#964;()</code> is applied. * @return <code>&#964;(p)</code> */ - public static F2mPoint Tau(F2mPoint p) + public static AbstractF2mPoint Tau(AbstractF2mPoint p) { return p.Tau(); } @@ -403,7 +403,7 @@ namespace Org.BouncyCastle.Math.EC.Abc * @throws ArgumentException if the given ECCurve is not a Koblitz * curve. */ - public static sbyte GetMu(F2mCurve curve) + public static sbyte GetMu(AbstractF2mCurve curve) { BigInteger a = curve.A.ToBigInteger(); @@ -423,6 +423,16 @@ namespace Org.BouncyCastle.Math.EC.Abc return mu; } + public static sbyte GetMu(ECFieldElement curveA) + { + return (sbyte)(curveA.IsZero ? -1 : 1); + } + + public static sbyte GetMu(int curveA) + { + return (sbyte)(curveA == 0 ? -1 : 1); + } + /** * Calculates the Lucas Sequence elements <code>U<sub>k-1</sub></code> and * <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code> and @@ -526,53 +536,60 @@ namespace Org.BouncyCastle.Math.EC.Abc * @throws ArgumentException if <code>curve</code> is not a * Koblitz curve (Anomalous Binary Curve, ABC). */ - public static BigInteger[] GetSi(F2mCurve curve) + public static BigInteger[] GetSi(AbstractF2mCurve curve) { if (!curve.IsKoblitz) throw new ArgumentException("si is defined for Koblitz curves only"); - int m = curve.M; + int m = curve.FieldSize; int a = curve.A.ToBigInteger().IntValue; - sbyte mu = curve.GetMu(); - int h = curve.Cofactor.IntValue; + sbyte mu = GetMu(a); + int shifts = GetShiftsForCofactor(curve.Cofactor); 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"); + ui[0] = ui[0].Negate(); + ui[1] = ui[1].Negate(); } - BigInteger[] si = new BigInteger[2]; + BigInteger dividend0 = BigInteger.One.Add(ui[1]).ShiftRight(shifts); + BigInteger dividend1 = BigInteger.One.Add(ui[0]).ShiftRight(shifts).Negate(); - if (h == 2) - { - si[0] = dividend0.ShiftRight(1); - si[1] = dividend1.ShiftRight(1).Negate(); - } - else if (h == 4) + return new BigInteger[] { dividend0, dividend1 }; + } + + public static BigInteger[] GetSi(int fieldSize, int curveA, BigInteger cofactor) + { + sbyte mu = GetMu(curveA); + int shifts = GetShiftsForCofactor(cofactor); + int index = fieldSize + 3 - curveA; + BigInteger[] ui = GetLucas(mu, index, false); + if (mu == 1) { - si[0] = dividend0.ShiftRight(2); - si[1] = dividend1.ShiftRight(2).Negate(); + ui[0] = ui[0].Negate(); + ui[1] = ui[1].Negate(); } - else + + BigInteger dividend0 = BigInteger.One.Add(ui[1]).ShiftRight(shifts); + BigInteger dividend1 = BigInteger.One.Add(ui[0]).ShiftRight(shifts).Negate(); + + return new BigInteger[] { dividend0, dividend1 }; + } + + protected static int GetShiftsForCofactor(BigInteger h) + { + if (h != null && h.BitLength < 4) { - throw new ArgumentException("h (Cofactor) must be 2 or 4"); + int hi = h.IntValue; + if (hi == 2) + return 1; + if (hi == 4) + return 2; } - return si; + throw new ArgumentException("h (Cofactor) must be 2 or 4"); } /** @@ -624,70 +641,77 @@ namespace Org.BouncyCastle.Math.EC.Abc } /** - * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint} * by a <code>BigInteger</code> using the reduced <code>&#964;</code>-adic * NAF (RTNAF) method. - * @param p The F2mPoint to Multiply. + * @param p The AbstractF2mPoint to Multiply. * @param k The <code>BigInteger</code> by which to Multiply <code>p</code>. * @return <code>k * p</code> */ - public static F2mPoint MultiplyRTnaf(F2mPoint p, BigInteger k) + public static AbstractF2mPoint MultiplyRTnaf(AbstractF2mPoint p, BigInteger k) { - F2mCurve curve = (F2mCurve) p.Curve; - int m = curve.M; - sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; - sbyte mu = curve.GetMu(); + AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve; + int m = curve.FieldSize; + int a = curve.A.ToBigInteger().IntValue; + sbyte mu = GetMu(a); BigInteger[] s = curve.GetSi(); - ZTauElement rho = PartModReduction(k, m, a, s, mu, (sbyte)10); + ZTauElement rho = PartModReduction(k, m, (sbyte)a, s, mu, (sbyte)10); return MultiplyTnaf(p, rho); } /** - * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint} * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> * using the <code>&#964;</code>-adic NAF (TNAF) method. - * @param p The F2mPoint to Multiply. + * @param p The AbstractF2mPoint to Multiply. * @param lambda The element <code>&#955;</code> of * <code><b>Z</b>[&#964;]</code>. * @return <code>&#955; * p</code> */ - public static F2mPoint MultiplyTnaf(F2mPoint p, ZTauElement lambda) + public static AbstractF2mPoint MultiplyTnaf(AbstractF2mPoint p, ZTauElement lambda) { - F2mCurve curve = (F2mCurve)p.Curve; - sbyte mu = curve.GetMu(); + AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve; + sbyte mu = GetMu(curve.A); sbyte[] u = TauAdicNaf(mu, lambda); - F2mPoint q = MultiplyFromTnaf(p, u); + AbstractF2mPoint q = MultiplyFromTnaf(p, u); return q; } /** - * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint} * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> * using the <code>&#964;</code>-adic NAF (TNAF) method, given the TNAF * of <code>&#955;</code>. - * @param p The F2mPoint to Multiply. + * @param p The AbstractF2mPoint to Multiply. * @param u The the TNAF of <code>&#955;</code>.. * @return <code>&#955; * p</code> */ - public static F2mPoint MultiplyFromTnaf(F2mPoint p, sbyte[] u) + public static AbstractF2mPoint MultiplyFromTnaf(AbstractF2mPoint p, sbyte[] u) { - F2mCurve curve = (F2mCurve)p.Curve; - F2mPoint q = (F2mPoint) curve.Infinity; + ECCurve curve = p.Curve; + AbstractF2mPoint q = (AbstractF2mPoint)curve.Infinity; + AbstractF2mPoint pNeg = (AbstractF2mPoint)p.Negate(); + int tauCount = 0; 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) + ++tauCount; + sbyte ui = u[i]; + if (ui != 0) { - q = (F2mPoint)q.SubtractSimple(p); + q = q.TauPow(tauCount); + tauCount = 0; + + ECPoint x = ui > 0 ? p : pNeg; + q = (AbstractF2mPoint)q.Add(x); } } + if (tauCount > 0) + { + q = q.TauPow(tauCount); + } return q; } @@ -800,28 +824,21 @@ namespace Org.BouncyCastle.Math.EC.Abc * @param a The parameter <code>a</code> of the elliptic curve. * @return The precomputation array for <code>p</code>. */ - public static F2mPoint[] GetPreComp(F2mPoint p, sbyte a) + public static AbstractF2mPoint[] GetPreComp(AbstractF2mPoint 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; - } + sbyte[][] alphaTnaf = (a == 0) ? Tnaf.Alpha0Tnaf : Tnaf.Alpha1Tnaf; + + AbstractF2mPoint[] pu = new AbstractF2mPoint[(uint)(alphaTnaf.Length + 1) >> 1]; + pu[0] = p; - int precompLen = alphaTnaf.Length; - for (int i = 3; i < precompLen; i = i + 2) + uint precompLen = (uint)alphaTnaf.Length; + for (uint i = 3; i < precompLen; i += 2) { - pu[i] = Tnaf.MultiplyFromTnaf(p, alphaTnaf[i]); + pu[i >> 1] = Tnaf.MultiplyFromTnaf(p, alphaTnaf[i]); } - + + p.Curve.NormalizeAll(pu); + return pu; } } diff --git a/crypto/src/math/ec/custom/djb/Curve25519.cs b/crypto/src/math/ec/custom/djb/Curve25519.cs
index 3dbdac051..6ed7c0648 100644 --- a/crypto/src/math/ec/custom/djb/Curve25519.cs +++ b/crypto/src/math/ec/custom/djb/Curve25519.cs
@@ -1,13 +1,12 @@ using System; -using Org.BouncyCastle.Math.EC.Custom.Sec; -using Org.BouncyCastle.Math.Field; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Djb { internal class Curve25519 - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = Nat256.ToBigInteger(Curve25519Field.P); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb protected readonly Curve25519Point m_infinity; public Curve25519() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new Curve25519Point(this, null, null); @@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb { return new Curve25519Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(A).Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new Curve25519Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/djb/Curve25519Field.cs b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
index 809e51b80..837821e1a 100644 --- a/crypto/src/math/ec/custom/djb/Curve25519Field.cs +++ b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
@@ -1,7 +1,7 @@ using System; using System.Diagnostics; -using Org.BouncyCastle.Math.EC.Custom.Sec; +using Org.BouncyCastle.Math.Raw; namespace Org.BouncyCastle.Math.EC.Custom.Djb { diff --git a/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
index 8d5a80326..732e9e468 100644 --- a/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs +++ b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
@@ -1,6 +1,6 @@ using System; -using Org.BouncyCastle.Math.EC.Custom.Sec; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Djb diff --git a/crypto/src/math/ec/custom/djb/Curve25519Point.cs b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
index f3da59d16..eb8fc12f2 100644 --- a/crypto/src/math/ec/custom/djb/Curve25519Point.cs +++ b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
@@ -1,11 +1,11 @@ using System; -using Org.BouncyCastle.Math.EC.Custom.Sec; +using Org.BouncyCastle.Math.Raw; namespace Org.BouncyCastle.Math.EC.Custom.Djb { internal class Curve25519Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -48,11 +48,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb return new Curve25519Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECFieldElement GetZCoord(int index) { if (index == 1) @@ -224,14 +219,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb return TwiceJacobianModified(false).Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs new file mode 100644
index 000000000..9da27b470 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP128R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF")); + + private const int SecP128R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP128R1Point m_infinity; + + public SecP128R1Curve() + : base(q) + { + this.m_infinity = new SecP128R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("E87579C11079F43DD824993C2CEE5ED3"))); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFE0000000075A30D1B9038A115")); + this.m_cofactor = BigInteger.One; + + this.m_coord = SecP128R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP128R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP128R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP128R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP128R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP128R1Field.cs b/crypto/src/math/ec/custom/sec/SecP128R1Field.cs new file mode 100644
index 000000000..ff6fb6b65 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP128R1Field.cs
@@ -0,0 +1,218 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP128R1Field + { + // 2^128 - 2^97 - 1 + internal static readonly uint[] P = new uint[] { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD }; + internal static readonly uint[] PExt = new uint[] { 0x00000001, 0x00000000, 0x00000000, 0x00000004, 0xFFFFFFFE, + 0xFFFFFFFF, 0x00000003, 0xFFFFFFFC }; + private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFB, + 0x00000001, 0x00000000, 0xFFFFFFFC, 0x00000003 }; + private const uint P3 = 0xFFFFFFFD; + private const uint PExt7 = 0xFFFFFFFC; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat128.Add(x, y, z); + if (c != 0 || (z[3] == P3 && Nat128.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat256.Add(xx, yy, zz); + if (c != 0 || (zz[7] == PExt7 && Nat256.Gte(zz, PExt))) + { + Nat.AddTo(PExtInv.Length, PExtInv, zz); + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(4, x, z); + if (c != 0 || (z[3] == P3 && Nat128.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat128.FromBigInteger(x); + if (z[3] == P3 && Nat128.Gte(z, P)) + { + Nat128.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(4, x, 0, z); + } + else + { + uint c = Nat128.Add(x, P, z); + Nat.ShiftDownBit(4, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat128.CreateExt(); + Nat128.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat128.MulAddTo(x, y, zz); + if (c != 0 || (zz[7] == PExt7 && Nat256.Gte(zz, PExt))) + { + Nat.AddTo(PExtInv.Length, PExtInv, zz); + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat128.IsZero(x)) + { + Nat128.Zero(z); + } + else + { + Nat128.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + ulong x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7]; + + x3 += x7; x6 += (x7 << 1); + x2 += x6; x5 += (x6 << 1); + x1 += x5; x4 += (x5 << 1); + x0 += x4; x3 += (x4 << 1); + + z[0] = (uint)x0; x1 += (x0 >> 32); + z[1] = (uint)x1; x2 += (x1 >> 32); + z[2] = (uint)x2; x3 += (x2 >> 32); + z[3] = (uint)x3; + + Reduce32((uint)(x3 >> 32), z); + } + + public static void Reduce32(uint x, uint[] z) + { + while (x != 0) + { + ulong c, x4 = x; + + c = (ulong)z[0] + x4; + z[0] = (uint)c; c >>= 32; + if (c != 0) + { + c += (ulong)z[1]; + z[1] = (uint)c; c >>= 32; + c += (ulong)z[2]; + z[2] = (uint)c; c >>= 32; + } + c += (ulong)z[3] + (x4 << 1); + z[3] = (uint)c; c >>= 32; + + Debug.Assert(c >= 0 && c <= 2); + + x = (uint)c; + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat128.CreateExt(); + Nat128.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat128.CreateExt(); + Nat128.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat128.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat128.Sub(x, y, z); + if (c != 0) + { + SubPInvFrom(z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(10, xx, yy, zz); + if (c != 0) + { + Nat.SubFrom(PExtInv.Length, PExtInv, zz); + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(4, x, 0, z); + if (c != 0 || (z[3] == P3 && Nat128.Gte(z, P))) + { + AddPInvTo(z); + } + } + + private static void AddPInvTo(uint[] z) + { + long c = (long)z[0] + 1; + z[0] = (uint)c; c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; c >>= 32; + c += (long)z[2]; + z[2] = (uint)c; c >>= 32; + } + c += (long)z[3] + 2; + z[3] = (uint)c; + } + + private static void SubPInvFrom(uint[] z) + { + long c = (long)z[0] - 1; + z[0] = (uint)c; c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; c >>= 32; + c += (long)z[2]; + z[2] = (uint)c; c >>= 32; + } + c += (long)z[3] - 2; + z[3] = (uint)c; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP128R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP128R1FieldElement.cs new file mode 100644
index 000000000..fa7951d5d --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP128R1FieldElement.cs
@@ -0,0 +1,198 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP128R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP128R1Curve.q; + + protected internal readonly uint[] x; + + public SecP128R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP128R1FieldElement", "x"); + + this.x = SecP128R1Field.FromBigInteger(x); + } + + public SecP128R1FieldElement() + { + this.x = Nat128.Create(); + } + + protected internal SecP128R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat128.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat128.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat128.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat128.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP128R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat128.Create(); + SecP128R1Field.Add(x, ((SecP128R1FieldElement)b).x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat128.Create(); + SecP128R1Field.AddOne(x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat128.Create(); + SecP128R1Field.Subtract(x, ((SecP128R1FieldElement)b).x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat128.Create(); + SecP128R1Field.Multiply(x, ((SecP128R1FieldElement)b).x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + // return multiply(b.invert()); + uint[] z = Nat128.Create(); + Mod.Invert(SecP128R1Field.P, ((SecP128R1FieldElement)b).x, z); + SecP128R1Field.Multiply(z, x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat128.Create(); + SecP128R1Field.Negate(x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat128.Create(); + SecP128R1Field.Square(x, z); + return new SecP128R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + // return new SecP128R1FieldElement(toBigInteger().modInverse(Q)); + uint[] z = Nat128.Create(); + Mod.Invert(SecP128R1Field.P, x, z); + return new SecP128R1FieldElement(z); + } + + // 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() + { + /* + * Raise this element to the exponent 2^126 - 2^95 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 31 1s } { 95 0s } + * + * Therefore we need an addition chain containing 31 (the length of the repunit) We use: + * 1, 2, 4, 8, 10, 20, 30, [31] + */ + + uint[] x1 = this.x; + if (Nat128.IsZero(x1) || Nat128.IsOne(x1)) + return this; + + uint[] x2 = Nat128.Create(); + SecP128R1Field.Square(x1, x2); + SecP128R1Field.Multiply(x2, x1, x2); + uint[] x4 = Nat128.Create(); + SecP128R1Field.SquareN(x2, 2, x4); + SecP128R1Field.Multiply(x4, x2, x4); + uint[] x8 = Nat128.Create(); + SecP128R1Field.SquareN(x4, 4, x8); + SecP128R1Field.Multiply(x8, x4, x8); + uint[] x10 = x4; + SecP128R1Field.SquareN(x8, 2, x10); + SecP128R1Field.Multiply(x10, x2, x10); + uint[] x20 = x2; + SecP128R1Field.SquareN(x10, 10, x20); + SecP128R1Field.Multiply(x20, x10, x20); + uint[] x30 = x8; + SecP128R1Field.SquareN(x20, 10, x30); + SecP128R1Field.Multiply(x30, x10, x30); + uint[] x31 = x10; + SecP128R1Field.Square(x30, x31); + SecP128R1Field.Multiply(x31, x1, x31); + + uint[] t1 = x31; + SecP128R1Field.SquareN(t1, 95, t1); + + uint[] t2 = x30; + SecP128R1Field.Square(t1, t2); + + return Nat128.Eq(x1, t2) ? new SecP128R1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP128R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP128R1FieldElement); + } + + public virtual bool Equals(SecP128R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat128.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 4); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP128R1Point.cs b/crypto/src/math/ec/custom/sec/SecP128R1Point.cs new file mode 100644
index 000000000..ae76d3cd1 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP128R1Point.cs
@@ -0,0 +1,279 @@ +using System; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP128R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP128R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(boolean)} + */ + public SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP128R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP128R1FieldElement X1 = (SecP128R1FieldElement)this.RawXCoord, Y1 = (SecP128R1FieldElement)this.RawYCoord; + SecP128R1FieldElement X2 = (SecP128R1FieldElement)b.RawXCoord, Y2 = (SecP128R1FieldElement)b.RawYCoord; + + SecP128R1FieldElement Z1 = (SecP128R1FieldElement)this.RawZCoords[0]; + SecP128R1FieldElement Z2 = (SecP128R1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat128.CreateExt(); + uint[] t2 = Nat128.Create(); + uint[] t3 = Nat128.Create(); + uint[] t4 = Nat128.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP128R1Field.Square(Z1.x, S2); + + U2 = t2; + SecP128R1Field.Multiply(S2, X2.x, U2); + + SecP128R1Field.Multiply(S2, Z1.x, S2); + SecP128R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP128R1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP128R1Field.Multiply(S1, X1.x, U1); + + SecP128R1Field.Multiply(S1, Z2.x, S1); + SecP128R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat128.Create(); + SecP128R1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP128R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat128.IsZero(H)) + { + if (Nat128.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP128R1Field.Square(H, HSquared); + + uint[] G = Nat128.Create(); + SecP128R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP128R1Field.Multiply(HSquared, U1, V); + + SecP128R1Field.Negate(G, G); + Nat128.Mul(S1, G, tt1); + + c = Nat128.AddBothTo(V, V, G); + SecP128R1Field.Reduce32(c, G); + + SecP128R1FieldElement X3 = new SecP128R1FieldElement(t4); + SecP128R1Field.Square(R, X3.x); + SecP128R1Field.Subtract(X3.x, G, X3.x); + + SecP128R1FieldElement Y3 = new SecP128R1FieldElement(G); + SecP128R1Field.Subtract(V, X3.x, Y3.x); + SecP128R1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP128R1Field.Reduce(tt1, Y3.x); + + SecP128R1FieldElement Z3 = new SecP128R1FieldElement(H); + if (!Z1IsOne) + { + SecP128R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP128R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP128R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP128R1FieldElement Y1 = (SecP128R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP128R1FieldElement X1 = (SecP128R1FieldElement)this.RawXCoord, Z1 = (SecP128R1FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat128.Create(); + uint[] t2 = Nat128.Create(); + + uint[] Y1Squared = Nat128.Create(); + SecP128R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat128.Create(); + SecP128R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP128R1Field.Square(Z1.x, Z1Squared); + } + + SecP128R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP128R1Field.Add(X1.x, Z1Squared, M); + SecP128R1Field.Multiply(M, t1, M); + c = Nat128.AddBothTo(M, M, M); + SecP128R1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP128R1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(4, S, 2, 0); + SecP128R1Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(4, T, 3, 0, t1); + SecP128R1Field.Reduce32(c, t1); + + SecP128R1FieldElement X3 = new SecP128R1FieldElement(T); + SecP128R1Field.Square(M, X3.x); + SecP128R1Field.Subtract(X3.x, S, X3.x); + SecP128R1Field.Subtract(X3.x, S, X3.x); + + SecP128R1FieldElement Y3 = new SecP128R1FieldElement(S); + SecP128R1Field.Subtract(S, X3.x, Y3.x); + SecP128R1Field.Multiply(Y3.x, M, Y3.x); + SecP128R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP128R1FieldElement Z3 = new SecP128R1FieldElement(M); + SecP128R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP128R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP128R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP128R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs new file mode 100644
index 000000000..7d45c6227 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs
@@ -0,0 +1,74 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160K1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = SecP160R2Curve.q; + + private const int SECP160K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP160K1Point m_infinity; + + public SecP160K1Curve() + : base(q) + { + this.m_infinity = new SecP160K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.ValueOf(7)); + this.m_order = new BigInteger(1, Hex.Decode("0100000000000000000001B8FA16DFAB9ACA16B6B3")); + this.m_cofactor = BigInteger.One; + this.m_coord = SECP160K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP160K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP160R2FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP160K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP160K1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160K1Point.cs b/crypto/src/math/ec/custom/sec/SecP160K1Point.cs new file mode 100644
index 000000000..1bcbadb33 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160K1Point.cs
@@ -0,0 +1,269 @@ +using System; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160K1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.CreatePoint to construct points + */ + public SecP160K1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP160K1Point(null, AffineXCoord, AffineYCoord); + } + + // B.3 pg 62 + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.RawXCoord, Y1 = (SecP160R2FieldElement)this.RawYCoord; + SecP160R2FieldElement X2 = (SecP160R2FieldElement)b.RawXCoord, Y2 = (SecP160R2FieldElement)b.RawYCoord; + + SecP160R2FieldElement Z1 = (SecP160R2FieldElement)this.RawZCoords[0]; + SecP160R2FieldElement Z2 = (SecP160R2FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat160.CreateExt(); + uint[] t2 = Nat160.Create(); + uint[] t3 = Nat160.Create(); + uint[] t4 = Nat160.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP160R2Field.Square(Z1.x, S2); + + U2 = t2; + SecP160R2Field.Multiply(S2, X2.x, U2); + + SecP160R2Field.Multiply(S2, Z1.x, S2); + SecP160R2Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP160R2Field.Square(Z2.x, S1); + + U1 = tt1; + SecP160R2Field.Multiply(S1, X1.x, U1); + + SecP160R2Field.Multiply(S1, Z2.x, S1); + SecP160R2Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat160.Create(); + SecP160R2Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP160R2Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat160.IsZero(H)) + { + if (Nat160.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP160R2Field.Square(H, HSquared); + + uint[] G = Nat160.Create(); + SecP160R2Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP160R2Field.Multiply(HSquared, U1, V); + + SecP160R2Field.Negate(G, G); + Nat160.Mul(S1, G, tt1); + + c = Nat160.AddBothTo(V, V, G); + SecP160R2Field.Reduce32(c, G); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(t4); + SecP160R2Field.Square(R, X3.x); + SecP160R2Field.Subtract(X3.x, G, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(G); + SecP160R2Field.Subtract(V, X3.x, Y3.x); + SecP160R2Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP160R2Field.Reduce(tt1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(H); + if (!Z1IsOne) + { + SecP160R2Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP160R2Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP160K1Point(curve, X3, Y3, zs, IsCompressed); + } + + // B.3 pg 62 + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP160R2FieldElement Y1 = (SecP160R2FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.RawXCoord, Z1 = (SecP160R2FieldElement)this.RawZCoords[0]; + + uint c; + + uint[] Y1Squared = Nat160.Create(); + SecP160R2Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat160.Create(); + SecP160R2Field.Square(Y1Squared, T); + + uint[] M = Nat160.Create(); + SecP160R2Field.Square(X1.x, M); + c = Nat160.AddBothTo(M, M, M); + SecP160R2Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP160R2Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(5, S, 2, 0); + SecP160R2Field.Reduce32(c, S); + + uint[] t1 = Nat160.Create(); + c = Nat.ShiftUpBits(5, T, 3, 0, t1); + SecP160R2Field.Reduce32(c, t1); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(T); + SecP160R2Field.Square(M, X3.x); + SecP160R2Field.Subtract(X3.x, S, X3.x); + SecP160R2Field.Subtract(X3.x, S, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(S); + SecP160R2Field.Subtract(S, X3.x, Y3.x); + SecP160R2Field.Multiply(Y3.x, M, Y3.x); + SecP160R2Field.Subtract(Y3.x, t1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(M); + SecP160R2Field.Twice(Y1.x, Z3.x); + if (!Z1.IsOne) + { + SecP160R2Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP160K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and threeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP160K1Point(Curve, this.RawXCoord, this.RawYCoord.Negate(), this.RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs new file mode 100644
index 000000000..87389af36 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF")); + + private const int SecP160R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP160R1Point m_infinity; + + public SecP160R1Curve() + : base(q) + { + this.m_infinity = new SecP160R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"))); + this.m_order = new BigInteger(1, Hex.Decode("0100000000000000000001F4C8F927AED3CA752257")); + this.m_cofactor = BigInteger.One; + + this.m_coord = SecP160R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP160R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP160R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP160R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP160R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R1Field.cs b/crypto/src/math/ec/custom/sec/SecP160R1Field.cs new file mode 100644
index 000000000..6a5a2ef64 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R1Field.cs
@@ -0,0 +1,186 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R1Field + { + // 2^160 - 2^31 - 1 + internal static readonly uint[] P = new uint[] { 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + internal static readonly uint[] PExt = new uint[] { 0x00000001, 0x40000001, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0xBFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000001, 0x00000001 }; + private const uint P4 = 0xFFFFFFFF; + private const uint PExt9 = 0xFFFFFFFF; + private const uint PInv = 0x80000001; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat160.Add(x, y, z); + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.AddWordTo(5, PInv, z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(10, xx, yy, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.Gte(10, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(10, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(5, x, z); + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.AddWordTo(5, PInv, z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat160.FromBigInteger(x); + if (z[4] == P4 && Nat160.Gte(z, P)) + { + Nat160.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(5, x, 0, z); + } + else + { + uint c = Nat160.Add(x, P, z); + Nat.ShiftDownBit(5, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat160.CreateExt(); + Nat160.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat160.MulAddTo(x, y, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.Gte(10, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(10, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat160.IsZero(x)) + { + Nat160.Zero(z); + } + else + { + Nat160.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong x5 = xx[5], x6 = xx[6], x7 = xx[7], x8 = xx[8], x9 = xx[9]; + + ulong c = 0; + c += (ulong)xx[0] + x5 + (x5 << 31); + z[0] = (uint)c; c >>= 32; + c += (ulong)xx[1] + x6 + (x6 << 31); + z[1] = (uint)c; c >>= 32; + c += (ulong)xx[2] + x7 + (x7 << 31); + z[2] = (uint)c; c >>= 32; + c += (ulong)xx[3] + x8 + (x8 << 31); + z[3] = (uint)c; c >>= 32; + c += (ulong)xx[4] + x9 + (x9 << 31); + z[4] = (uint)c; c >>= 32; + + Debug.Assert(c >> 32 == 0); + + Reduce32((uint)c, z); + } + + public static void Reduce32(uint x, uint[] z) + { + if ((x != 0 && Nat160.MulWordsAdd(PInv, x, z, 0) != 0) + || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.AddWordTo(5, PInv, z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat160.CreateExt(); + Nat160.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat160.CreateExt(); + Nat160.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat160.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat160.Sub(x, y, z); + if (c != 0) + { + Nat.SubWordFrom(5, PInv, z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(10, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(10, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(5, x, 0, z); + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.AddWordTo(5, PInv, z); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP160R1FieldElement.cs new file mode 100644
index 000000000..d1fc75644 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R1FieldElement.cs
@@ -0,0 +1,203 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP160R1Curve.q; + + protected internal readonly uint[] x; + + public SecP160R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP160R1FieldElement", "x"); + + this.x = SecP160R1Field.FromBigInteger(x); + } + + public SecP160R1FieldElement() + { + this.x = Nat160.Create(); + } + + protected internal SecP160R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat160.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat160.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat160.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat160.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP160R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat160.Create(); + SecP160R1Field.Add(x, ((SecP160R1FieldElement)b).x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat160.Create(); + SecP160R1Field.AddOne(x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat160.Create(); + SecP160R1Field.Subtract(x, ((SecP160R1FieldElement)b).x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat160.Create(); + SecP160R1Field.Multiply(x, ((SecP160R1FieldElement)b).x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + // return multiply(b.invert()); + uint[] z = Nat160.Create(); + Mod.Invert(SecP160R1Field.P, ((SecP160R1FieldElement)b).x, z); + SecP160R1Field.Multiply(z, x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat160.Create(); + SecP160R1Field.Negate(x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat160.Create(); + SecP160R1Field.Square(x, z); + return new SecP160R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + // return new SecP160R1FieldElement(ToBigInteger().modInverse(Q)); + uint[] z = Nat160.Create(); + Mod.Invert(SecP160R1Field.P, x, z); + return new SecP160R1FieldElement(z); + } + + // 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() + { + /* + * Raise this element to the exponent 2^158 - 2^29 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 129 1s } { 29 0s } + * + * Therefore we need an addition chain containing 129 (the length of the repunit) We use: + * 1, 2, 4, 8, 16, 32, 64, 128, [129] + */ + + uint[] x1 = this.x; + if (Nat160.IsZero(x1) || Nat160.IsOne(x1)) + { + return this; + } + + uint[] x2 = Nat160.Create(); + SecP160R1Field.Square(x1, x2); + SecP160R1Field.Multiply(x2, x1, x2); + uint[] x4 = Nat160.Create(); + SecP160R1Field.SquareN(x2, 2, x4); + SecP160R1Field.Multiply(x4, x2, x4); + uint[] x8 = x2; + SecP160R1Field.SquareN(x4, 4, x8); + SecP160R1Field.Multiply(x8, x4, x8); + uint[] x16 = x4; + SecP160R1Field.SquareN(x8, 8, x16); + SecP160R1Field.Multiply(x16, x8, x16); + uint[] x32 = x8; + SecP160R1Field.SquareN(x16, 16, x32); + SecP160R1Field.Multiply(x32, x16, x32); + uint[] x64 = x16; + SecP160R1Field.SquareN(x32, 32, x64); + SecP160R1Field.Multiply(x64, x32, x64); + uint[] x128 = x32; + SecP160R1Field.SquareN(x64, 64, x128); + SecP160R1Field.Multiply(x128, x64, x128); + uint[] x129 = x64; + SecP160R1Field.Square(x128, x129); + SecP160R1Field.Multiply(x129, x1, x129); + + uint[] t1 = x129; + SecP160R1Field.SquareN(t1, 29, t1); + + uint[] t2 = x128; + SecP160R1Field.Square(t1, t2); + + return Nat160.Eq(x1, t2) ? new SecP160R1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP160R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP160R1FieldElement); + } + + public virtual bool Equals(SecP160R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat160.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 5); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R1Point.cs b/crypto/src/math/ec/custom/sec/SecP160R1Point.cs new file mode 100644
index 000000000..f9f065de6 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R1Point.cs
@@ -0,0 +1,279 @@ +using System; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.CreatePoint to construct points + */ + public SecP160R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP160R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP160R1FieldElement X1 = (SecP160R1FieldElement)this.RawXCoord, Y1 = (SecP160R1FieldElement)this.RawYCoord; + SecP160R1FieldElement X2 = (SecP160R1FieldElement)b.RawXCoord, Y2 = (SecP160R1FieldElement)b.RawYCoord; + + SecP160R1FieldElement Z1 = (SecP160R1FieldElement)this.RawZCoords[0]; + SecP160R1FieldElement Z2 = (SecP160R1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat160.CreateExt(); + uint[] t2 = Nat160.Create(); + uint[] t3 = Nat160.Create(); + uint[] t4 = Nat160.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP160R1Field.Square(Z1.x, S2); + + U2 = t2; + SecP160R1Field.Multiply(S2, X2.x, U2); + + SecP160R1Field.Multiply(S2, Z1.x, S2); + SecP160R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP160R1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP160R1Field.Multiply(S1, X1.x, U1); + + SecP160R1Field.Multiply(S1, Z2.x, S1); + SecP160R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat160.Create(); + SecP160R1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP160R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat160.IsZero(H)) + { + if (Nat160.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP160R1Field.Square(H, HSquared); + + uint[] G = Nat160.Create(); + SecP160R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP160R1Field.Multiply(HSquared, U1, V); + + SecP160R1Field.Negate(G, G); + Nat160.Mul(S1, G, tt1); + + c = Nat160.AddBothTo(V, V, G); + SecP160R1Field.Reduce32(c, G); + + SecP160R1FieldElement X3 = new SecP160R1FieldElement(t4); + SecP160R1Field.Square(R, X3.x); + SecP160R1Field.Subtract(X3.x, G, X3.x); + + SecP160R1FieldElement Y3 = new SecP160R1FieldElement(G); + SecP160R1Field.Subtract(V, X3.x, Y3.x); + SecP160R1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP160R1Field.Reduce(tt1, Y3.x); + + SecP160R1FieldElement Z3 = new SecP160R1FieldElement(H); + if (!Z1IsOne) + { + SecP160R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP160R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP160R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP160R1FieldElement Y1 = (SecP160R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP160R1FieldElement X1 = (SecP160R1FieldElement)this.RawXCoord, Z1 = (SecP160R1FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat160.Create(); + uint[] t2 = Nat160.Create(); + + uint[] Y1Squared = Nat160.Create(); + SecP160R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat160.Create(); + SecP160R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP160R1Field.Square(Z1.x, Z1Squared); + } + + SecP160R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP160R1Field.Add(X1.x, Z1Squared, M); + SecP160R1Field.Multiply(M, t1, M); + c = Nat160.AddBothTo(M, M, M); + SecP160R1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP160R1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(5, S, 2, 0); + SecP160R1Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(5, T, 3, 0, t1); + SecP160R1Field.Reduce32(c, t1); + + SecP160R1FieldElement X3 = new SecP160R1FieldElement(T); + SecP160R1Field.Square(M, X3.x); + SecP160R1Field.Subtract(X3.x, S, X3.x); + SecP160R1Field.Subtract(X3.x, S, X3.x); + + SecP160R1FieldElement Y3 = new SecP160R1FieldElement(S); + SecP160R1Field.Subtract(S, X3.x, Y3.x); + SecP160R1Field.Multiply(Y3.x, M, Y3.x); + SecP160R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP160R1FieldElement Z3 = new SecP160R1FieldElement(M); + SecP160R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP160R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP160R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP160R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs b/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs new file mode 100644
index 000000000..100561453 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R2Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73")); + + private const int SecP160R2_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP160R2Point m_infinity; + + public SecP160R2Curve() + : base(q) + { + this.m_infinity = new SecP160R2Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("B4E134D3FB59EB8BAB57274904664D5AF50388BA"))); + this.m_order = new BigInteger(1, Hex.Decode("0100000000000000000000351EE786A818F3A1A16B")); + this.m_cofactor = BigInteger.One; + + this.m_coord = SecP160R2_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP160R2Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP160R2FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP160R2Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP160R2Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R2Field.cs b/crypto/src/math/ec/custom/sec/SecP160R2Field.cs new file mode 100644
index 000000000..1bef32eea --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R2Field.cs
@@ -0,0 +1,178 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R2Field + { + // 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFAC73, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x1B44BBA9, 0x0000A71A, 0x00000001, 0x00000000, 0x00000000, + 0xFFFF58E6, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xE4BB4457, 0xFFFF58E5, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0000A719, 0x00000002 }; + private const uint P4 = 0xFFFFFFFF; + private const uint PExt9 = 0xFFFFFFFF; + private const uint PInv33 = 0x538D; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat160.Add(x, y, z); + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.Add33To(5, PInv33, z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(10, xx, yy, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.Gte(10, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(10, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(5, x, z); + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.Add33To(5, PInv33, z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat160.FromBigInteger(x); + if (z[4] == P4 && Nat160.Gte(z, P)) + { + Nat160.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(5, x, 0, z); + } + else + { + uint c = Nat160.Add(x, P, z); + Nat.ShiftDownBit(5, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat160.CreateExt(); + Nat160.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat160.MulAddTo(x, y, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.Gte(10, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(10, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat160.IsZero(x)) + { + Nat160.Zero(z); + } + else + { + Nat160.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong cc = Nat160.Mul33Add(PInv33, xx, 5, xx, 0, z, 0); + uint c = Nat160.Mul33DWordAdd(PInv33, cc, z, 0); + + Debug.Assert(c == 0 || c == 1); + + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.Add33To(5, PInv33, z); + } + } + + public static void Reduce32(uint x, uint[] z) + { + if ((x != 0 && Nat160.Mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.Add33To(5, PInv33, z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat160.CreateExt(); + Nat160.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat160.CreateExt(); + Nat160.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat160.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat160.Sub(x, y, z); + if (c != 0) + { + Nat.Sub33From(5, PInv33, z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(10, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(10, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(5, x, 0, z); + if (c != 0 || (z[4] == P4 && Nat160.Gte(z, P))) + { + Nat.Add33To(5, PInv33, z); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R2FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP160R2FieldElement.cs new file mode 100644
index 000000000..bdb5245b2 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R2FieldElement.cs
@@ -0,0 +1,218 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R2FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP160R2Curve.q; + + protected internal readonly uint[] x; + + public SecP160R2FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP160R2FieldElement", "x"); + + this.x = SecP160R2Field.FromBigInteger(x); + } + + public SecP160R2FieldElement() + { + this.x = Nat160.Create(); + } + + protected internal SecP160R2FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat160.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat160.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat160.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat160.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP160R2Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat160.Create(); + SecP160R2Field.Add(x, ((SecP160R2FieldElement)b).x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat160.Create(); + SecP160R2Field.AddOne(x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat160.Create(); + SecP160R2Field.Subtract(x, ((SecP160R2FieldElement)b).x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat160.Create(); + SecP160R2Field.Multiply(x, ((SecP160R2FieldElement)b).x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + // return Multiply(b.invert()); + uint[] z = Nat160.Create(); + Mod.Invert(SecP160R2Field.P, ((SecP160R2FieldElement)b).x, z); + SecP160R2Field.Multiply(z, x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat160.Create(); + SecP160R2Field.Negate(x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat160.Create(); + SecP160R2Field.Square(x, z); + return new SecP160R2FieldElement(z); + } + + public override ECFieldElement Invert() + { + // return new SecP160R2FieldElement(ToBigInteger().modInverse(Q)); + uint[] z = Nat160.Create(); + Mod.Invert(SecP160R2Field.P, x, z); + return new SecP160R2FieldElement(z); + } + + // 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() + { + /* + * Raise this element to the exponent 2^158 - 2^30 - 2^12 - 2^10 - 2^7 - 2^6 - 2^5 - 2^1 - 2^0 + * + * Breaking up the exponent's binary representation into "repunits", we get: { 127 1s } { 1 + * 0s } { 17 1s } { 1 0s } { 1 1s } { 1 0s } { 2 1s } { 3 0s } { 3 1s } { 1 0s } { 1 1s } + * + * Therefore we need an Addition chain containing 1, 2, 3, 17, 127 (the lengths of the repunits) + * We use: [1], [2], [3], 4, 7, 14, [17], 31, 62, 124, [127] + */ + + uint[] x1 = this.x; + if (Nat160.IsZero(x1) || Nat160.IsOne(x1)) + { + return this; + } + + uint[] x2 = Nat160.Create(); + SecP160R2Field.Square(x1, x2); + SecP160R2Field.Multiply(x2, x1, x2); + uint[] x3 = Nat160.Create(); + SecP160R2Field.Square(x2, x3); + SecP160R2Field.Multiply(x3, x1, x3); + uint[] x4 = Nat160.Create(); + SecP160R2Field.Square(x3, x4); + SecP160R2Field.Multiply(x4, x1, x4); + uint[] x7 = Nat160.Create(); + SecP160R2Field.SquareN(x4, 3, x7); + SecP160R2Field.Multiply(x7, x3, x7); + uint[] x14 = x4; + SecP160R2Field.SquareN(x7, 7, x14); + SecP160R2Field.Multiply(x14, x7, x14); + uint[] x17 = x7; + SecP160R2Field.SquareN(x14, 3, x17); + SecP160R2Field.Multiply(x17, x3, x17); + uint[] x31 = Nat160.Create(); + SecP160R2Field.SquareN(x17, 14, x31); + SecP160R2Field.Multiply(x31, x14, x31); + uint[] x62 = x14; + SecP160R2Field.SquareN(x31, 31, x62); + SecP160R2Field.Multiply(x62, x31, x62); + uint[] x124 = x31; + SecP160R2Field.SquareN(x62, 62, x124); + SecP160R2Field.Multiply(x124, x62, x124); + uint[] x127 = x62; + SecP160R2Field.SquareN(x124, 3, x127); + SecP160R2Field.Multiply(x127, x3, x127); + + uint[] t1 = x127; + SecP160R2Field.SquareN(t1, 18, t1); + SecP160R2Field.Multiply(t1, x17, t1); + SecP160R2Field.SquareN(t1, 2, t1); + SecP160R2Field.Multiply(t1, x1, t1); + SecP160R2Field.SquareN(t1, 3, t1); + SecP160R2Field.Multiply(t1, x2, t1); + SecP160R2Field.SquareN(t1, 6, t1); + SecP160R2Field.Multiply(t1, x3, t1); + SecP160R2Field.SquareN(t1, 2, t1); + SecP160R2Field.Multiply(t1, x1, t1); + + uint[] t2 = x2; + SecP160R2Field.Square(t1, t2); + + return Nat160.Eq(x1, t2) ? new SecP160R2FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP160R2FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP160R2FieldElement); + } + + public virtual bool Equals(SecP160R2FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat160.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 5); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP160R2Point.cs b/crypto/src/math/ec/custom/sec/SecP160R2Point.cs new file mode 100644
index 000000000..343cf8c16 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP160R2Point.cs
@@ -0,0 +1,279 @@ +using System; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP160R2Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.CreatePoint to construct points + */ + public SecP160R2Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP160R2Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.RawXCoord, Y1 = (SecP160R2FieldElement)this.RawYCoord; + SecP160R2FieldElement X2 = (SecP160R2FieldElement)b.RawXCoord, Y2 = (SecP160R2FieldElement)b.RawYCoord; + + SecP160R2FieldElement Z1 = (SecP160R2FieldElement)this.RawZCoords[0]; + SecP160R2FieldElement Z2 = (SecP160R2FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat160.CreateExt(); + uint[] t2 = Nat160.Create(); + uint[] t3 = Nat160.Create(); + uint[] t4 = Nat160.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP160R2Field.Square(Z1.x, S2); + + U2 = t2; + SecP160R2Field.Multiply(S2, X2.x, U2); + + SecP160R2Field.Multiply(S2, Z1.x, S2); + SecP160R2Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP160R2Field.Square(Z2.x, S1); + + U1 = tt1; + SecP160R2Field.Multiply(S1, X1.x, U1); + + SecP160R2Field.Multiply(S1, Z2.x, S1); + SecP160R2Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat160.Create(); + SecP160R2Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP160R2Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat160.IsZero(H)) + { + if (Nat160.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP160R2Field.Square(H, HSquared); + + uint[] G = Nat160.Create(); + SecP160R2Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP160R2Field.Multiply(HSquared, U1, V); + + SecP160R2Field.Negate(G, G); + Nat160.Mul(S1, G, tt1); + + c = Nat160.AddBothTo(V, V, G); + SecP160R2Field.Reduce32(c, G); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(t4); + SecP160R2Field.Square(R, X3.x); + SecP160R2Field.Subtract(X3.x, G, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(G); + SecP160R2Field.Subtract(V, X3.x, Y3.x); + SecP160R2Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP160R2Field.Reduce(tt1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(H); + if (!Z1IsOne) + { + SecP160R2Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP160R2Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP160R2Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP160R2FieldElement Y1 = (SecP160R2FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.RawXCoord, Z1 = (SecP160R2FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat160.Create(); + uint[] t2 = Nat160.Create(); + + uint[] Y1Squared = Nat160.Create(); + SecP160R2Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat160.Create(); + SecP160R2Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP160R2Field.Square(Z1.x, Z1Squared); + } + + SecP160R2Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP160R2Field.Add(X1.x, Z1Squared, M); + SecP160R2Field.Multiply(M, t1, M); + c = Nat160.AddBothTo(M, M, M); + SecP160R2Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP160R2Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(5, S, 2, 0); + SecP160R2Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(5, T, 3, 0, t1); + SecP160R2Field.Reduce32(c, t1); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(T); + SecP160R2Field.Square(M, X3.x); + SecP160R2Field.Subtract(X3.x, S, X3.x); + SecP160R2Field.Subtract(X3.x, S, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(S); + SecP160R2Field.Subtract(S, X3.x, Y3.x); + SecP160R2Field.Multiply(Y3.x, M, Y3.x); + SecP160R2Field.Subtract(Y3.x, t1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(M); + SecP160R2Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP160R2Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP160R2Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP160R2Point(Curve, this.RawXCoord, this.RawYCoord.Negate(), this.RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
index 54b87588b..81f77197e 100644 --- a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP192K1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP192K1Point m_infinity; public SecP192K1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP192K1Point(this, null, null); @@ -72,27 +71,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP192K1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP192K1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Field.cs b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
index d5ca903d1..a00360360 100644 --- a/crypto/src/math/ec/custom/sec/SecP192K1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP192K1Field diff --git a/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs
index 78886dd8c..dce377035 100644 --- a/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs
@@ -1,6 +1,7 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Point.cs b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
index 561324f8e..58eb09102 100644 --- a/crypto/src/math/ec/custom/sec/SecP192K1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP192K1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -55,11 +57,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP192K1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -259,14 +256,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
index 57b20d31e..cb3a981c8 100644 --- a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP192R1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP192R1Point m_infinity; public SecP192R1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP192R1Point(this, null, null); @@ -75,27 +74,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP192R1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(A).Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP192R1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Field.cs b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
index 85e3a0394..096c2b51f 100644 --- a/crypto/src/math/ec/custom/sec/SecP192R1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP192R1Field diff --git a/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs
index 020c5cdbb..45bcb00f0 100644 --- a/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Point.cs b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
index c249c1269..3b53e341e 100644 --- a/crypto/src/math/ec/custom/sec/SecP192R1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP192R1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -54,11 +56,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP192R1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -271,14 +268,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
index 70de308bb..d4be7d8de 100644 --- a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP224K1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP224K1Point m_infinity; public SecP224K1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP224K1Point(this, null, null); @@ -72,27 +71,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP224K1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP224K1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Field.cs b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
index a55810c6d..98cf777a5 100644 --- a/crypto/src/math/ec/custom/sec/SecP224K1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP224K1Field diff --git a/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs
index 72ff4b099..fec07436a 100644 --- a/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs
@@ -1,6 +1,7 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Point.cs b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
index dd6faa829..98cb29274 100644 --- a/crypto/src/math/ec/custom/sec/SecP224K1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP224K1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -55,11 +57,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP224K1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -259,14 +256,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
index 33b66be82..cda8781ff 100644 --- a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP224R1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP224R1Point m_infinity; public SecP224R1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP224R1Point(this, null, null); @@ -75,27 +74,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP224R1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(A).Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP224R1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Field.cs b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
index 559593c66..4f5c3bbda 100644 --- a/crypto/src/math/ec/custom/sec/SecP224R1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP224R1Field diff --git a/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs
index 06f47cded..2b9a06564 100644 --- a/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Point.cs b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
index 3b339720d..73c4f1948 100644 --- a/crypto/src/math/ec/custom/sec/SecP224R1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP224R1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -54,11 +56,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP224R1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -271,14 +268,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
index 89de61706..59e2cefb2 100644 --- a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP256K1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP256K1Point m_infinity; public SecP256K1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP256K1Point(this, null, null); @@ -72,27 +71,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP256K1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP256K1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Field.cs b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
index ba3a070a9..b0646e93f 100644 --- a/crypto/src/math/ec/custom/sec/SecP256K1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP256K1Field diff --git a/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs
index d9a039a4f..473113d0f 100644 --- a/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs
@@ -1,6 +1,7 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Point.cs b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
index b12eadb72..072a0b969 100644 --- a/crypto/src/math/ec/custom/sec/SecP256K1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP256K1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -55,11 +57,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP256K1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -259,14 +256,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
index 9a94eb8d1..6b3448f06 100644 --- a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP256R1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP256R1Point m_infinity; public SecP256R1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP256R1Point(this, null, null); @@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP256R1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(A).Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP256R1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Field.cs b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
index 9ed9dcd41..11594b2ba 100644 --- a/crypto/src/math/ec/custom/sec/SecP256R1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP256R1Field diff --git a/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs
index b22763cfa..d7838aead 100644 --- a/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Point.cs b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
index 0e4b95a10..83320824d 100644 --- a/crypto/src/math/ec/custom/sec/SecP256R1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP256R1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -54,11 +56,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP256R1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -271,14 +268,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
index f3dec05c9..7fd58276a 100644 --- a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP384R1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP384R1Point m_infinity; public SecP384R1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP384R1Point(this, null, null); @@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP384R1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(A).Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP384R1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Field.cs b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
index 508b01e3c..7820775ee 100644 --- a/crypto/src/math/ec/custom/sec/SecP384R1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP384R1Field diff --git a/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs
index 40086978d..18d48a57d 100644 --- a/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Point.cs b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
index 1ca8489dc..83159ce61 100644 --- a/crypto/src/math/ec/custom/sec/SecP384R1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP384R1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -54,11 +56,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP384R1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -272,14 +269,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
index cb42304ef..e5083c7f0 100644 --- a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs +++ b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
@@ -1,12 +1,11 @@ using System; -using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP521R1Curve - : ECCurve + : AbstractFpCurve { public static readonly BigInteger q = new BigInteger(1, Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")); @@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec protected readonly SecP521R1Point m_infinity; public SecP521R1Curve() - : base(FiniteFields.GetPrimeField(q)) + : base(q) { this.m_infinity = new SecP521R1Point(this, null, null); @@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec { return new SecP521R1Point(this, x, y, zs, withCompression); } - - protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) - { - ECFieldElement x = FromBigInteger(X1); - ECFieldElement alpha = x.Square().Add(A).Multiply(x).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"); - - if (beta.TestBitZero() != (yTilde == 1)) - { - // Use the other root - beta = beta.Negate(); - } - - return new SecP521R1Point(this, x, beta, true); - } } } diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Field.cs b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
index 3568156d8..b7f8eb146 100644 --- a/crypto/src/math/ec/custom/sec/SecP521R1Field.cs +++ b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
@@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP521R1Field diff --git a/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs
index 83a615928..6f02a7eb5 100644 --- a/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs +++ b/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Custom.Sec diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Point.cs b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
index 44d590f08..7ad97f76f 100644 --- a/crypto/src/math/ec/custom/sec/SecP521R1Point.cs +++ b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
@@ -1,9 +1,11 @@ using System; +using Org.BouncyCastle.Math.Raw; + namespace Org.BouncyCastle.Math.EC.Custom.Sec { internal class SecP521R1Point - : ECPointBase + : AbstractFpPoint { /** * Create a point which encodes with point compression. @@ -54,11 +56,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new SecP521R1Point(null, AffineXCoord, AffineYCoord); } - protected internal override bool CompressionYTilde - { - get { return this.AffineYCoord.TestBitZero(); } - } - public override ECPoint Add(ECPoint b) { if (this.IsInfinity) @@ -267,14 +264,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return Twice().Add(this); } - public override ECPoint Subtract(ECPoint b) - { - if (b.IsInfinity) - return this; - - return Add(b.Negate()); - } - public override ECPoint Negate() { if (IsInfinity) diff --git a/crypto/src/math/ec/custom/sec/SecT113Field.cs b/crypto/src/math/ec/custom/sec/SecT113Field.cs new file mode 100644
index 000000000..640c6e787 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT113Field.cs
@@ -0,0 +1,209 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT113Field + { + private const ulong M49 = ulong.MaxValue >> 15; + private const ulong M57 = ulong.MaxValue >> 7; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat128.FromBigInteger64(x); + Reduce15(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat128.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion + + ulong[] t0 = Nat128.Create64(); + ulong[] t1 = Nat128.Create64(); + + Square(x, t0); + Multiply(t0, x, t0); + Square(t0, t0); + Multiply(t0, x, t0); + SquareN(t0, 3, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 7, t0); + Multiply(t0, t1, t0); + SquareN(t0, 14, t1); + Multiply(t1, t0, t1); + SquareN(t1, 28, t0); + Multiply(t0, t1, t0); + SquareN(t0, 56, t1); + Multiply(t1, t0, t1); + Square(t1, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat128.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat128.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + + x1 ^= (x3 << 15) ^ (x3 << 24); + x2 ^= (x3 >> 49) ^ (x3 >> 40); + + x0 ^= (x2 << 15) ^ (x2 << 24); + x1 ^= (x2 >> 49) ^ (x2 >> 40); + + ulong t = x1 >> 49; + z[0] = x0 ^ t ^ (t << 9); + z[1] = x1 & M49; + } + + public static void Reduce15(ulong[] z, int zOff) + { + ulong z1 = z[zOff + 1], t = z1 >> 49; + z[zOff ] ^= t ^ (t << 9); + z[zOff + 1] = z1 & M49; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat128.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat128.CreateExt64(); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat128.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + ulong f0 = x[0], f1 = x[1]; + f1 = ((f0 >> 57) ^ (f1 << 7)) & M57; + f0 &= M57; + + ulong g0 = y[0], g1 = y[1]; + g1 = ((g0 >> 57) ^ (g1 << 7)) & M57; + g0 &= M57; + + ulong[] H = new ulong[6]; + + ImplMulw(f0, g0, H, 0); // H(0) 57/56 bits + ImplMulw(f1, g1, H, 2); // H(INF) 57/54 bits + ImplMulw(f0 ^ f1, g0 ^ g1, H, 4); // H(1) 57/56 bits + + ulong r = H[1] ^ H[2]; + ulong z0 = H[0], + z3 = H[3], + z1 = H[4] ^ z0 ^ r, + z2 = H[5] ^ z3 ^ r; + + zz[0] = z0 ^ (z1 << 57); + zz[1] = (z1 >> 7) ^ (z2 << 50); + zz[2] = (z2 >> 14) ^ (z3 << 43); + zz[3] = (z3 >> 21); + } + + protected static void ImplMulw(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 57 == 0); + Debug.Assert(y >> 57 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7]; + int k = 48; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3 + ^ u[(j >> 6) & 7] << 6; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 9) > 0); + + h ^= ((x & 0x0100804020100800UL) & (ulong)(((long)y << 7) >> 63)) >> 8; + + Debug.Assert(h >> 49 == 0); + + z[zOff ] = l & M57; + z[zOff + 1] = (l >> 57) ^ (h << 7); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + Interleave.Expand64To128(x[0], zz, 0); + Interleave.Expand64To128(x[1], zz, 2); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT113FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT113FieldElement.cs new file mode 100644
index 000000000..f217e28cb --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT113FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT113FieldElement + : ECFieldElement + { + protected internal readonly ulong[] x; + + public SecT113FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 113) + throw new ArgumentException("value invalid for SecT113FieldElement", "x"); + + this.x = SecT113Field.FromBigInteger(x); + } + + public SecT113FieldElement() + { + this.x = Nat128.Create64(); + } + + protected internal SecT113FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat128.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat128.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1L) != 0L; + } + + public override BigInteger ToBigInteger() + { + return Nat128.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT113Field"; } + } + + public override int FieldSize + { + get { return 113; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat128.Create64(); + SecT113Field.Add(x, ((SecT113FieldElement)b).x, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat128.Create64(); + SecT113Field.AddOne(x, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and Subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat128.Create64(); + SecT113Field.Multiply(x, ((SecT113FieldElement)b).x, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT113FieldElement)b).x; + ulong[] xx = ((SecT113FieldElement)x).x, yx = ((SecT113FieldElement)y).x; + + ulong[] tt = Nat128.CreateExt64(); + SecT113Field.MultiplyAddToExt(ax, bx, tt); + SecT113Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat128.Create64(); + SecT113Field.Reduce(tt, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat128.Create64(); + SecT113Field.Square(x, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT113FieldElement)x).x, yx = ((SecT113FieldElement)y).x; + + ulong[] tt = Nat128.CreateExt64(); + SecT113Field.SquareAddToExt(ax, tt); + SecT113Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat128.Create64(); + SecT113Field.Reduce(tt, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat128.Create64(); + SecT113Field.SquareN(x, pow, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat128.Create64(); + SecT113Field.Invert(x, z); + return new SecT113FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Tpb; } + } + + public virtual int M + { + get { return 113; } + } + + public virtual int K1 + { + get { return 9; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT113FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT113FieldElement); + } + + public virtual bool Equals(SecT113FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat128.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 113009 ^ Arrays.GetHashCode(x, 0, 2); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs new file mode 100644
index 000000000..2705c94aa --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT113R1Curve + : AbstractF2mCurve + { + private const int SecT113R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT113R1Point m_infinity; + + public SecT113R1Curve() + : base(113, 9, 0, 0) + { + this.m_infinity = new SecT113R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("003088250CA6E7C7FE649CE85820F7"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("00E8BEE4D3E2260744188BE0E9C723"))); + this.m_order = new BigInteger(1, Hex.Decode("0100000000000000D9CCEC8A39E56F")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT113R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT113R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 113; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT113FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT113R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT113R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 113; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 9; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT113R1Point.cs b/crypto/src/math/ec/custom/sec/SecT113R1Point.cs new file mode 100644
index 000000000..6ecc8b01a --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT113R1Point.cs
@@ -0,0 +1,281 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT113R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT113R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT113R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT113R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT113R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT113R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT113R1Point(Curve, X, L.Add(Z), new ECFieldElement[]{ Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs new file mode 100644
index 000000000..abfd26d5b --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT113R2Curve + : AbstractF2mCurve + { + private const int SecT113R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT113R2Point m_infinity; + + public SecT113R2Curve() + : base(113, 9, 0, 0) + { + this.m_infinity = new SecT113R2Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("00689918DBEC7E5A0DD6DFC0AA55C7"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("0095E9A9EC9B297BD4BF36E059184F"))); + this.m_order = new BigInteger(1, Hex.Decode("010000000000000108789B2496AF93")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT113R2_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT113R2Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 113; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT113FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT113R2Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT113R2Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 113; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 9; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT113R2Point.cs b/crypto/src/math/ec/custom/sec/SecT113R2Point.cs new file mode 100644
index 000000000..1453d78c3 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT113R2Point.cs
@@ -0,0 +1,291 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT113R2Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT113R2Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + { + return b; + } + if (b.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT113R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT113R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT113R2Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + { + return b; + } + if (b.IsInfinity) + { + return Twice(); + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT113R2Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT113R2Point(Curve, X, L.Add(Z), new ECFieldElement[]{ Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT131Field.cs b/crypto/src/math/ec/custom/sec/SecT131Field.cs new file mode 100644
index 000000000..47f97078c --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT131Field.cs
@@ -0,0 +1,303 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT131Field + { + private const ulong M03 = ulong.MaxValue >> 61; + private const ulong M44 = ulong.MaxValue >> 20; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat192.FromBigInteger64(x); + Reduce61(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat192.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion + + ulong[] t0 = Nat192.Create64(); + ulong[] t1 = Nat192.Create64(); + + Square(x, t0); + Multiply(t0, x, t0); + SquareN(t0, 2, t1); + Multiply(t1, t0, t1); + SquareN(t1, 4, t0); + Multiply(t0, t1, t0); + SquareN(t0, 8, t1); + Multiply(t1, t0, t1); + SquareN(t1, 16, t0); + Multiply(t0, t1, t0); + SquareN(t0, 32, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 65, t0); + Multiply(t0, t1, t0); + Square(t0, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat192.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat192.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4]; + + x1 ^= (x4 << 61) ^ (x4 << 63); + x2 ^= (x4 >> 3) ^ (x4 >> 1) ^ x4 ^ (x4 << 5); + x3 ^= (x4 >> 59); + + x0 ^= (x3 << 61) ^ (x3 << 63); + x1 ^= (x3 >> 3) ^ (x3 >> 1) ^ x3 ^ (x3 << 5); + x2 ^= (x3 >> 59); + + ulong t = x2 >> 3; + z[0] = x0 ^ t ^ (t << 2) ^ (t << 3) ^ (t << 8); + z[1] = x1 ^ (t >> 56); + z[2] = x2 & M03; + } + + public static void Reduce61(ulong[] z, int zOff) + { + ulong z2 = z[zOff + 2], t = z2 >> 3; + z[zOff ] ^= t ^ (t << 2) ^ (t << 3) ^ (t << 8); + z[zOff + 1] ^= (t >> 56); + z[zOff + 2] = z2 & M03; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat.Create64(5); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat.Create64(5); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat.Create64(5); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5]; + zz[0] = z0 ^ (z1 << 44); + zz[1] = (z1 >> 20) ^ (z2 << 24); + zz[2] = (z2 >> 40) ^ (z3 << 4) + ^ (z4 << 48); + zz[3] = (z3 >> 60) ^ (z5 << 28) + ^ (z4 >> 16); + zz[4] = (z5 >> 36); + zz[5] = 0; + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * "Five-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + ulong f0 = x[0], f1 = x[1], f2 = x[2]; + f2 = ((f1 >> 24) ^ (f2 << 40)) & M44; + f1 = ((f0 >> 44) ^ (f1 << 20)) & M44; + f0 &= M44; + + ulong g0 = y[0], g1 = y[1], g2 = y[2]; + g2 = ((g1 >> 24) ^ (g2 << 40)) & M44; + g1 = ((g0 >> 44) ^ (g1 << 20)) & M44; + g0 &= M44; + + ulong[] H = new ulong[10]; + + ImplMulw(f0, g0, H, 0); // H(0) 44/43 bits + ImplMulw(f2, g2, H, 2); // H(INF) 44/41 bits + + ulong t0 = f0 ^ f1 ^ f2; + ulong t1 = g0 ^ g1 ^ g2; + + ImplMulw(t0, t1, H, 4); // H(1) 44/43 bits + + ulong t2 = (f1 << 1) ^ (f2 << 2); + ulong t3 = (g1 << 1) ^ (g2 << 2); + + ImplMulw(f0 ^ t2, g0 ^ t3, H, 6); // H(t) 44/45 bits + ImplMulw(t0 ^ t2, t1 ^ t3, H, 8); // H(t + 1) 44/45 bits + + ulong t4 = H[6] ^ H[8]; + ulong t5 = H[7] ^ H[9]; + + Debug.Assert(t5 >> 44 == 0); + + // Calculate V + ulong v0 = (t4 << 1) ^ H[6]; + ulong v1 = t4 ^ (t5 << 1) ^ H[7]; + ulong v2 = t5; + + // Calculate U + ulong u0 = H[0]; + ulong u1 = H[1] ^ H[0] ^ H[4]; + ulong u2 = H[1] ^ H[5]; + + // Calculate W + ulong w0 = u0 ^ v0 ^ (H[2] << 4) ^ (H[2] << 1); + ulong w1 = u1 ^ v1 ^ (H[3] << 4) ^ (H[3] << 1); + ulong w2 = u2 ^ v2; + + // Propagate carries + w1 ^= (w0 >> 44); w0 &= M44; + w2 ^= (w1 >> 44); w1 &= M44; + + Debug.Assert((w0 & 1UL) == 0); + + // Divide W by t + + w0 = (w0 >> 1) ^ ((w1 & 1UL) << 43); + w1 = (w1 >> 1) ^ ((w2 & 1UL) << 43); + w2 = (w2 >> 1); + + // Divide W by (t + 1) + + w0 ^= (w0 << 1); + w0 ^= (w0 << 2); + w0 ^= (w0 << 4); + w0 ^= (w0 << 8); + w0 ^= (w0 << 16); + w0 ^= (w0 << 32); + + w0 &= M44; w1 ^= (w0 >> 43); + + w1 ^= (w1 << 1); + w1 ^= (w1 << 2); + w1 ^= (w1 << 4); + w1 ^= (w1 << 8); + w1 ^= (w1 << 16); + w1 ^= (w1 << 32); + + w1 &= M44; w2 ^= (w1 >> 43); + + w2 ^= (w2 << 1); + w2 ^= (w2 << 2); + w2 ^= (w2 << 4); + w2 ^= (w2 << 8); + w2 ^= (w2 << 16); + w2 ^= (w2 << 32); + + Debug.Assert(w2 >> 42 == 0); + + zz[0] = u0; + zz[1] = u1 ^ w0 ^ H[2]; + zz[2] = u2 ^ w1 ^ w0 ^ H[3]; + zz[3] = w2 ^ w1; + zz[4] = w2 ^ H[2]; + zz[5] = H[3]; + + ImplCompactExt(zz); + } + + protected static void ImplMulw(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 45 == 0); + Debug.Assert(y >> 45 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7] + ^ u[(j >> 3) & 7] << 3 + ^ u[(j >> 6) & 7] << 6; + int k = 33; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3 + ^ u[(j >> 6) & 7] << 6 + ^ u[(j >> 9) & 7] << 9; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 12) > 0); + + Debug.Assert(h >> 25 == 0); + + z[zOff ] = l & M44; + z[zOff + 1] = (l >> 44) ^ (h << 20); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + Interleave.Expand64To128(x[0], zz, 0); + Interleave.Expand64To128(x[1], zz, 2); + + zz[4] = Interleave.Expand8to16((uint)x[2]); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs new file mode 100644
index 000000000..0ea00ea07 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT131FieldElement + : ECFieldElement + { + protected readonly ulong[] x; + + public SecT131FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 131) + throw new ArgumentException("value invalid for SecT131FieldElement", "x"); + + this.x = SecT131Field.FromBigInteger(x); + } + + public SecT131FieldElement() + { + this.x = Nat192.Create64(); + } + + protected internal SecT131FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat192.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat192.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1UL) != 0UL; + } + + public override BigInteger ToBigInteger() + { + return Nat192.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT131Field"; } + } + + public override int FieldSize + { + get { return 131; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat192.Create64(); + SecT131Field.Add(x, ((SecT131FieldElement)b).x, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat192.Create64(); + SecT131Field.AddOne(x, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and Subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat192.Create64(); + SecT131Field.Multiply(x, ((SecT131FieldElement)b).x, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT131FieldElement)b).x; + ulong[] xx = ((SecT131FieldElement)x).x, yx = ((SecT131FieldElement)y).x; + + ulong[] tt = Nat.Create64(5); + SecT131Field.MultiplyAddToExt(ax, bx, tt); + SecT131Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat192.Create64(); + SecT131Field.Reduce(tt, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat192.Create64(); + SecT131Field.Square(x, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT131FieldElement)x).x, yx = ((SecT131FieldElement)y).x; + + ulong[] tt = Nat.Create64(5); + SecT131Field.SquareAddToExt(ax, tt); + SecT131Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat192.Create64(); + SecT131Field.Reduce(tt, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat192.Create64(); + SecT131Field.SquareN(x, pow, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat192.Create64(); + SecT131Field.Invert(x, z); + return new SecT131FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Ppb; } + } + + public virtual int M + { + get { return 131; } + } + + public virtual int K1 + { + get { return 2; } + } + + public virtual int K2 + { + get { return 3; } + } + + public virtual int K3 + { + get { return 8; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT131FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT131FieldElement); + } + + public virtual bool Equals(SecT131FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat192.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 131832 ^ Arrays.GetHashCode(x, 0, 3); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs new file mode 100644
index 000000000..b73964c39 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT131R1Curve + : AbstractF2mCurve + { + private const int SecT131R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT131R1Point m_infinity; + + public SecT131R1Curve() + : base(131, 2, 3, 8) + { + this.m_infinity = new SecT131R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("07A11B09A76B562144418FF3FF8C2570B8"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("0217C05610884B63B9C6C7291678F9D341"))); + this.m_order = new BigInteger(1, Hex.Decode("0400000000000000023123953A9464B54D")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT131R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT131R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 131; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT131FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT131R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT131R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 131; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 2; } + } + + public virtual int K2 + { + get { return 3; } + } + + public virtual int K3 + { + get { return 8; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT131R1Point.cs b/crypto/src/math/ec/custom/sec/SecT131R1Point.cs new file mode 100644
index 000000000..7afdad89c --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT131R1Point.cs
@@ -0,0 +1,287 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT131R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT131R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT131R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT131R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT131R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT131R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT131R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + { + return b; + } + if (b.IsInfinity) + { + return Twice(); + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT131R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT131R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT131R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs new file mode 100644
index 000000000..724921c94 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT131R2Curve + : AbstractF2mCurve + { + private const int SecT131R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT131R2Point m_infinity; + + public SecT131R2Curve() + : base(131, 2, 3, 8) + { + this.m_infinity = new SecT131R2Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("03E5A88919D7CAFCBF415F07C2176573B2"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("04B8266A46C55657AC734CE38F018F2192"))); + this.m_order = new BigInteger(1, Hex.Decode("0400000000000000016954A233049BA98F")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT131R2_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT131R2Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override int FieldSize + { + get { return 131; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT131FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT131R2Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT131R2Point(this, x, y, zs, withCompression); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 131; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 2; } + } + + public virtual int K2 + { + get { return 3; } + } + + public virtual int K3 + { + get { return 8; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT131R2Point.cs b/crypto/src/math/ec/custom/sec/SecT131R2Point.cs new file mode 100644
index 000000000..be61561da --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT131R2Point.cs
@@ -0,0 +1,283 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT131R2Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT131R2Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT131R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT131R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT131R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT131R2Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT131R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT131R2Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT131R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT131R2Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163Field.cs b/crypto/src/math/ec/custom/sec/SecT163Field.cs new file mode 100644
index 000000000..f921a5bc7 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163Field.cs
@@ -0,0 +1,313 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163Field + { + private const ulong M35 = ulong.MaxValue >> 29; + private const ulong M55 = ulong.MaxValue >> 9; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat192.FromBigInteger64(x); + Reduce29(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat192.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion with bases { 2, 3 } + + ulong[] t0 = Nat192.Create64(); + ulong[] t1 = Nat192.Create64(); + + Square(x, t0); + + // 3 | 162 + SquareN(t0, 1, t1); + Multiply(t0, t1, t0); + SquareN(t1, 1, t1); + Multiply(t0, t1, t0); + + // 3 | 54 + SquareN(t0, 3, t1); + Multiply(t0, t1, t0); + SquareN(t1, 3, t1); + Multiply(t0, t1, t0); + + // 3 | 18 + SquareN(t0, 9, t1); + Multiply(t0, t1, t0); + SquareN(t1, 9, t1); + Multiply(t0, t1, t0); + + // 3 | 6 + SquareN(t0, 27, t1); + Multiply(t0, t1, t0); + SquareN(t1, 27, t1); + Multiply(t0, t1, t0); + + // 2 | 2 + SquareN(t0, 81, t1); + Multiply(t0, t1, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat192.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat192.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4], x5 = xx[5]; + + x2 ^= (x5 << 29) ^ (x5 << 32) ^ (x5 << 35) ^ (x5 << 36); + x3 ^= (x5 >> 35) ^ (x5 >> 32) ^ (x5 >> 29) ^ (x5 >> 28); + + x1 ^= (x4 << 29) ^ (x4 << 32) ^ (x4 << 35) ^ (x4 << 36); + x2 ^= (x4 >> 35) ^ (x4 >> 32) ^ (x4 >> 29) ^ (x4 >> 28); + + x0 ^= (x3 << 29) ^ (x3 << 32) ^ (x3 << 35) ^ (x3 << 36); + x1 ^= (x3 >> 35) ^ (x3 >> 32) ^ (x3 >> 29) ^ (x3 >> 28); + + ulong t = x2 >> 35; + z[0] = x0 ^ t ^ (t << 3) ^ (t << 6) ^ (t << 7); + z[1] = x1; + z[2] = x2 & M35; + } + + public static void Reduce29(ulong[] z, int zOff) + { + ulong z2 = z[zOff + 2], t = z2 >> 35; + z[zOff ] ^= t ^ (t << 3) ^ (t << 6) ^ (t << 7); + z[zOff + 2] = z2 & M35; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat192.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat192.CreateExt64(); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat192.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5]; + zz[0] = z0 ^ (z1 << 55); + zz[1] = (z1 >> 9) ^ (z2 << 46); + zz[2] = (z2 >> 18) ^ (z3 << 37); + zz[3] = (z3 >> 27) ^ (z4 << 28); + zz[4] = (z4 >> 36) ^ (z5 << 19); + zz[5] = (z5 >> 45); + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * "Five-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + ulong f0 = x[0], f1 = x[1], f2 = x[2]; + f2 = ((f1 >> 46) ^ (f2 << 18)); + f1 = ((f0 >> 55) ^ (f1 << 9)) & M55; + f0 &= M55; + + ulong g0 = y[0], g1 = y[1], g2 = y[2]; + g2 = ((g1 >> 46) ^ (g2 << 18)); + g1 = ((g0 >> 55) ^ (g1 << 9)) & M55; + g0 &= M55; + + ulong[] H = new ulong[10]; + + ImplMulw(f0, g0, H, 0); // H(0) 55/54 bits + ImplMulw(f2, g2, H, 2); // H(INF) 55/50 bits + + ulong t0 = f0 ^ f1 ^ f2; + ulong t1 = g0 ^ g1 ^ g2; + + ImplMulw(t0, t1, H, 4); // H(1) 55/54 bits + + ulong t2 = (f1 << 1) ^ (f2 << 2); + ulong t3 = (g1 << 1) ^ (g2 << 2); + + ImplMulw(f0 ^ t2, g0 ^ t3, H, 6); // H(t) 55/56 bits + ImplMulw(t0 ^ t2, t1 ^ t3, H, 8); // H(t + 1) 55/56 bits + + ulong t4 = H[6] ^ H[8]; + ulong t5 = H[7] ^ H[9]; + + Debug.Assert(t5 >> 55 == 0); + + // Calculate V + ulong v0 = (t4 << 1) ^ H[6]; + ulong v1 = t4 ^ (t5 << 1) ^ H[7]; + ulong v2 = t5; + + // Calculate U + ulong u0 = H[0]; + ulong u1 = H[1] ^ H[0] ^ H[4]; + ulong u2 = H[1] ^ H[5]; + + // Calculate W + ulong w0 = u0 ^ v0 ^ (H[2] << 4) ^ (H[2] << 1); + ulong w1 = u1 ^ v1 ^ (H[3] << 4) ^ (H[3] << 1); + ulong w2 = u2 ^ v2; + + // Propagate carries + w1 ^= (w0 >> 55); w0 &= M55; + w2 ^= (w1 >> 55); w1 &= M55; + + Debug.Assert((w0 & 1UL) == 0UL); + + // Divide W by t + + w0 = (w0 >> 1) ^ ((w1 & 1UL) << 54); + w1 = (w1 >> 1) ^ ((w2 & 1UL) << 54); + w2 = (w2 >> 1); + + // Divide W by (t + 1) + + w0 ^= (w0 << 1); + w0 ^= (w0 << 2); + w0 ^= (w0 << 4); + w0 ^= (w0 << 8); + w0 ^= (w0 << 16); + w0 ^= (w0 << 32); + + w0 &= M55; w1 ^= (w0 >> 54); + + w1 ^= (w1 << 1); + w1 ^= (w1 << 2); + w1 ^= (w1 << 4); + w1 ^= (w1 << 8); + w1 ^= (w1 << 16); + w1 ^= (w1 << 32); + + w1 &= M55; w2 ^= (w1 >> 54); + + w2 ^= (w2 << 1); + w2 ^= (w2 << 2); + w2 ^= (w2 << 4); + w2 ^= (w2 << 8); + w2 ^= (w2 << 16); + w2 ^= (w2 << 32); + + Debug.Assert(w2 >> 52 == 0); + + zz[0] = u0; + zz[1] = u1 ^ w0 ^ H[2]; + zz[2] = u2 ^ w1 ^ w0 ^ H[3]; + zz[3] = w2 ^ w1; + zz[4] = w2 ^ H[2]; + zz[5] = H[3]; + + ImplCompactExt(zz); + } + + protected static void ImplMulw(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 56 == 0); + Debug.Assert(y >> 56 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 3]; + int k = 47; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3 + ^ u[(j >> 6) & 7] << 6; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 9) > 0); + + Debug.Assert(h >> 47 == 0); + + z[zOff ] = l & M55; + z[zOff + 1] = (l >> 55) ^ (h << 9); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + Interleave.Expand64To128(x[0], zz, 0); + Interleave.Expand64To128(x[1], zz, 2); + + ulong x2 = x[2]; + zz[4] = Interleave.Expand32to64((uint)x2); + zz[5] = Interleave.Expand8to16((uint)(x2 >> 32)); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs new file mode 100644
index 000000000..c7a0b5639 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163FieldElement + : ECFieldElement + { + protected readonly ulong[] x; + + public SecT163FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 163) + throw new ArgumentException("value invalid for SecT163FieldElement", "x"); + + this.x = SecT163Field.FromBigInteger(x); + } + + public SecT163FieldElement() + { + this.x = Nat192.Create64(); + } + + protected internal SecT163FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat192.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat192.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1L) != 0L; + } + + public override BigInteger ToBigInteger() + { + return Nat192.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT163Field"; } + } + + public override int FieldSize + { + get { return 163; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat192.Create64(); + SecT163Field.Add(x, ((SecT163FieldElement)b).x, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat192.Create64(); + SecT163Field.AddOne(x, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat192.Create64(); + SecT163Field.Multiply(x, ((SecT163FieldElement)b).x, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT163FieldElement)b).x; + ulong[] xx = ((SecT163FieldElement)x).x, yx = ((SecT163FieldElement)y).x; + + ulong[] tt = Nat192.CreateExt64(); + SecT163Field.MultiplyAddToExt(ax, bx, tt); + SecT163Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat192.Create64(); + SecT163Field.Reduce(tt, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat192.Create64(); + SecT163Field.Square(x, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT163FieldElement)x).x, yx = ((SecT163FieldElement)y).x; + + ulong[] tt = Nat192.CreateExt64(); + SecT163Field.SquareAddToExt(ax, tt); + SecT163Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat192.Create64(); + SecT163Field.Reduce(tt, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat192.Create64(); + SecT163Field.SquareN(x, pow, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat192.Create64(); + SecT163Field.Invert(x, z); + return new SecT163FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Ppb; } + } + + public virtual int M + { + get { return 163; } + } + + public virtual int K1 + { + get { return 3; } + } + + public virtual int K2 + { + get { return 6; } + } + + public virtual int K3 + { + get { return 7; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT163FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT163FieldElement); + } + + public virtual bool Equals(SecT163FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat192.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 163763 ^ Arrays.GetHashCode(x, 0, 3); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs new file mode 100644
index 000000000..68ff646ca --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163K1Curve + : AbstractF2mCurve + { + private const int SecT163K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT163K1Point m_infinity; + + public SecT163K1Curve() + : base(163, 3, 6, 7) + { + this.m_infinity = new SecT163K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.One); + this.m_b = this.m_a; + this.m_order = new BigInteger(1, Hex.Decode("04000000000000000000020108A2E0CC0D99F8A5EF")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT163K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT163K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected override ECMultiplier CreateDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 163; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT163FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT163K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT163K1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return true; } + } + + public virtual int M + { + get { return 163; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 3; } + } + + public virtual int K2 + { + get { return 6; } + } + + public virtual int K3 + { + get { return 7; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163K1Point.cs b/crypto/src/math/ec/custom/sec/SecT163K1Point.cs new file mode 100644
index 000000000..2e3ba57d0 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163K1Point.cs
@@ -0,0 +1,289 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163K1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT163K1Point(null, this.AffineXCoord, this.AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.getA()); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + //return new SecT163K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT163K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT163K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT163K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT163K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T = L1.Square().Add(L1Z1).Add(Z1Sq); + if (T.IsZero) + { + //return new SecT163K1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT163K1Point(curve, T, curve.B, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(X3); + + return new SecT163K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + // NOTE: TwicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.getA().Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = Z1Sq.Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.getA().Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT163K1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT163K1Point(curve, A, curve.B, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT163K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT163K1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs new file mode 100644
index 000000000..8ae58ccef --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163R1Curve + : AbstractF2mCurve + { + private const int SecT163R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT163R1Point m_infinity; + + public SecT163R1Curve() + : base(163, 3, 6, 7) + { + this.m_infinity = new SecT163R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9"))); + this.m_order = new BigInteger(1, Hex.Decode("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT163R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT163R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 163; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT163FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT163R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT163R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 163; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 3; } + } + + public virtual int K2 + { + get { return 6; } + } + + public virtual int K3 + { + get { return 7; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163R1Point.cs b/crypto/src/math/ec/custom/sec/SecT163R1Point.cs new file mode 100644
index 000000000..811a09f14 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163R1Point.cs
@@ -0,0 +1,283 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT163R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT163R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT163R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT163R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT163R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT163R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT163R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT163R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT163R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs new file mode 100644
index 000000000..5a4fa5ad1 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163R2Curve + : AbstractF2mCurve + { + private const int SecT163R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT163R2Point m_infinity; + + public SecT163R2Curve() + : base(163, 3, 6, 7) + { + this.m_infinity = new SecT163R2Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.One); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("020A601907B8C953CA1481EB10512F78744A3205FD"))); + this.m_order = new BigInteger(1, Hex.Decode("040000000000000000000292FE77E70C12A4234C33")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT163R2_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT163R2Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 163; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT163FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT163R2Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT163R2Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 163; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 3; } + } + + public virtual int K2 + { + get { return 6; } + } + + public virtual int K3 + { + get { return 7; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT163R2Point.cs b/crypto/src/math/ec/custom/sec/SecT163R2Point.cs new file mode 100644
index 000000000..07b3f1fd9 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT163R2Point.cs
@@ -0,0 +1,290 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT163R2Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT163R2Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + { + return Twice(); + } + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + return new SecT163R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT163R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT163R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T = L1.Square().Add(L1Z1).Add(Z1Sq); + if (T.IsZero) + { + return new SecT163R2Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT163R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + { + return b; + } + if (b.IsInfinity) + { + return Twice(); + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = Z1Sq.Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT163R2Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT163R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT163R2Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT193Field.cs b/crypto/src/math/ec/custom/sec/SecT193Field.cs new file mode 100644
index 000000000..5154f1e0a --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT193Field.cs
@@ -0,0 +1,282 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT193Field + { + private const ulong M01 = 1UL; + private const ulong M49 = ulong.MaxValue >> 15; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat256.FromBigInteger64(x); + Reduce63(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat256.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion with bases { 2, 3 } + + ulong[] t0 = Nat256.Create64(); + ulong[] t1 = Nat256.Create64(); + + Square(x, t0); + + // 3 | 192 + SquareN(t0, 1, t1); + Multiply(t0, t1, t0); + SquareN(t1, 1, t1); + Multiply(t0, t1, t0); + + // 2 | 64 + SquareN(t0, 3, t1); + Multiply(t0, t1, t0); + + // 2 | 32 + SquareN(t0, 6, t1); + Multiply(t0, t1, t0); + + // 2 | 16 + SquareN(t0, 12, t1); + Multiply(t0, t1, t0); + + // 2 | 8 + SquareN(t0, 24, t1); + Multiply(t0, t1, t0); + + // 2 | 4 + SquareN(t0, 48, t1); + Multiply(t0, t1, t0); + + // 2 | 2 + SquareN(t0, 96, t1); + Multiply(t0, t1, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat256.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat256.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4], x5 = xx[5], x6 = xx[6]; + + x2 ^= (x6 << 63); + x3 ^= (x6 >> 1) ^ (x6 << 14); + x4 ^= (x6 >> 50); + + x1 ^= (x5 << 63); + x2 ^= (x5 >> 1) ^ (x5 << 14); + x3 ^= (x5 >> 50); + + x0 ^= (x4 << 63); + x1 ^= (x4 >> 1) ^ (x4 << 14); + x2 ^= (x4 >> 50); + + ulong t = x3 >> 1; + z[0] = x0 ^ t ^ (t << 15); + z[1] = x1 ^ (t >> 49); + z[2] = x2; + z[3] = x3 & M01; + } + + public static void Reduce63(ulong[] z, int zOff) + { + ulong z3 = z[zOff + 3], t = z3 >> 1; + z[zOff ] ^= t ^ (t << 15); + z[zOff + 1] ^= (t >> 49); + z[zOff + 3] = z3 & M01; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7]; + zz[0] = z0 ^ (z1 << 49); + zz[1] = (z1 >> 15) ^ (z2 << 34); + zz[2] = (z2 >> 30) ^ (z3 << 19); + zz[3] = (z3 >> 45) ^ (z4 << 4) + ^ (z5 << 53); + zz[4] = (z4 >> 60) ^ (z6 << 38) + ^ (z5 >> 11); + zz[5] = (z6 >> 26) ^ (z7 << 23); + zz[6] = (z7 >> 41); + zz[7] = 0; + } + + protected static void ImplExpand(ulong[] x, ulong[] z) + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = x0 & M49; + z[1] = ((x0 >> 49) ^ (x1 << 15)) & M49; + z[2] = ((x1 >> 34) ^ (x2 << 30)) & M49; + z[3] = ((x2 >> 19) ^ (x3 << 45)); + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + ulong[] f = new ulong[4], g = new ulong[4]; + ImplExpand(x, f); + ImplExpand(y, g); + + ImplMulwAcc(f[0], g[0], zz, 0); + ImplMulwAcc(f[1], g[1], zz, 1); + ImplMulwAcc(f[2], g[2], zz, 2); + ImplMulwAcc(f[3], g[3], zz, 3); + + // U *= (1 - t^n) + for (int i = 5; i > 0; --i) + { + zz[i] ^= zz[i - 1]; + } + + ImplMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1); + ImplMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3); + + // V *= (1 - t^2n) + for (int i = 7; i > 1; --i) + { + zz[i] ^= zz[i - 2]; + } + + // Double-length recursion + { + ulong c0 = f[0] ^ f[2], c1 = f[1] ^ f[3]; + ulong d0 = g[0] ^ g[2], d1 = g[1] ^ g[3]; + ImplMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3); + ulong[] t = new ulong[3]; + ImplMulwAcc(c0, d0, t, 0); + ImplMulwAcc(c1, d1, t, 1); + ulong t0 = t[0], t1 = t[1], t2 = t[2]; + zz[2] ^= t0; + zz[3] ^= t0 ^ t1; + zz[4] ^= t2 ^ t1; + zz[5] ^= t2; + } + + ImplCompactExt(zz); + } + + protected static void ImplMulwAcc(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 49 == 0); + Debug.Assert(y >> 49 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7] + ^ (u[(j >> 3) & 7] << 3); + int k = 36; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3 + ^ u[(j >> 6) & 7] << 6 + ^ u[(j >> 9) & 7] << 9 + ^ u[(j >> 12) & 7] << 12; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 15) > 0); + + Debug.Assert(h >> 33 == 0); + + z[zOff ] ^= l & M49; + z[zOff + 1] ^= (l >> 49) ^ (h << 15); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + Interleave.Expand64To128(x[0], zz, 0); + Interleave.Expand64To128(x[1], zz, 2); + Interleave.Expand64To128(x[2], zz, 4); + zz[6] = (x[3] & M01); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs new file mode 100644
index 000000000..eba4d10e6 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT193FieldElement + : ECFieldElement + { + protected readonly ulong[] x; + + public SecT193FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 193) + throw new ArgumentException("value invalid for SecT193FieldElement", "x"); + + this.x = SecT193Field.FromBigInteger(x); + } + + public SecT193FieldElement() + { + this.x = Nat256.Create64(); + } + + protected internal SecT193FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat256.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat256.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1UL) != 0UL; + } + + public override BigInteger ToBigInteger() + { + return Nat256.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT193Field"; } + } + + public override int FieldSize + { + get { return 193; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat256.Create64(); + SecT193Field.Add(x, ((SecT193FieldElement)b).x, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat256.Create64(); + SecT193Field.AddOne(x, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and Subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat256.Create64(); + SecT193Field.Multiply(x, ((SecT193FieldElement)b).x, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT193FieldElement)b).x; + ulong[] xx = ((SecT193FieldElement)x).x, yx = ((SecT193FieldElement)y).x; + + ulong[] tt = Nat256.CreateExt64(); + SecT193Field.MultiplyAddToExt(ax, bx, tt); + SecT193Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat256.Create64(); + SecT193Field.Reduce(tt, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat256.Create64(); + SecT193Field.Square(x, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT193FieldElement)x).x, yx = ((SecT193FieldElement)y).x; + + ulong[] tt = Nat256.CreateExt64(); + SecT193Field.SquareAddToExt(ax, tt); + SecT193Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat256.Create64(); + SecT193Field.Reduce(tt, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat256.Create64(); + SecT193Field.SquareN(x, pow, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat256.Create64(); + SecT193Field.Invert(x, z); + return new SecT193FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Tpb; } + } + + public virtual int M + { + get { return 193; } + } + + public virtual int K1 + { + get { return 15; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT193FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT193FieldElement); + } + + public virtual bool Equals(SecT193FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat256.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 1930015 ^ Arrays.GetHashCode(x, 0, 4); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs new file mode 100644
index 000000000..a2cb5a8ac --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT193R1Curve + : AbstractF2mCurve + { + private const int SecT193R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT193R1Point m_infinity; + + public SecT193R1Curve() + : base(193, 15, 0, 0) + { + this.m_infinity = new SecT193R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814"))); + this.m_order = new BigInteger(1, Hex.Decode("01000000000000000000000000C7F34A778F443ACC920EBA49")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT193R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT193R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 193; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT193FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT193R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT193R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 193; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 15; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT193R1Point.cs b/crypto/src/math/ec/custom/sec/SecT193R1Point.cs new file mode 100644
index 000000000..062fce9d4 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT193R1Point.cs
@@ -0,0 +1,283 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT193R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT193R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT193R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT193R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT193R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT193R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT193R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT193R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT193R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT193R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs new file mode 100644
index 000000000..1c84a3eac --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT193R2Curve + : AbstractF2mCurve + { + private const int SecT193R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT193R2Point m_infinity; + + public SecT193R2Curve() + : base(193, 15, 0, 0) + { + this.m_infinity = new SecT193R2Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, Hex.Decode("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B"))); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE"))); + this.m_order = new BigInteger(1, Hex.Decode("010000000000000000000000015AAB561B005413CCD4EE99D5")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT193R2_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT193R2Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 193; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT193FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT193R2Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT193R2Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 193; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 15; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT193R2Point.cs b/crypto/src/math/ec/custom/sec/SecT193R2Point.cs new file mode 100644
index 000000000..18d89e316 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT193R2Point.cs
@@ -0,0 +1,283 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT193R2Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT193R2Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new SecT193R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT193R2Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT193R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new SecT193R2Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT193R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT193R2Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT193R2Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT193R2Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT233Field.cs b/crypto/src/math/ec/custom/sec/SecT233Field.cs new file mode 100644
index 000000000..a2f73fd5d --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT233Field.cs
@@ -0,0 +1,276 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT233Field + { + private const ulong M41 = ulong.MaxValue >> 23; + private const ulong M59 = ulong.MaxValue >> 5; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + zz[7] = xx[7] ^ yy[7]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat256.FromBigInteger64(x); + Reduce23(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat256.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion + + ulong[] t0 = Nat256.Create64(); + ulong[] t1 = Nat256.Create64(); + + Square(x, t0); + Multiply(t0, x, t0); + Square(t0, t0); + Multiply(t0, x, t0); + SquareN(t0, 3, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 7, t0); + Multiply(t0, t1, t0); + SquareN(t0, 14, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 29, t0); + Multiply(t0, t1, t0); + SquareN(t0, 58, t1); + Multiply(t1, t0, t1); + SquareN(t1, 116, t0); + Multiply(t0, t1, t0); + Square(t0, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat256.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat256.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + ulong x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7]; + + x3 ^= (x7 << 23); + x4 ^= (x7 >> 41) ^ (x7 << 33); + x5 ^= (x7 >> 31); + + x2 ^= (x6 << 23); + x3 ^= (x6 >> 41) ^ (x6 << 33); + x4 ^= (x6 >> 31); + + x1 ^= (x5 << 23); + x2 ^= (x5 >> 41) ^ (x5 << 33); + x3 ^= (x5 >> 31); + + x0 ^= (x4 << 23); + x1 ^= (x4 >> 41) ^ (x4 << 33); + x2 ^= (x4 >> 31); + + ulong t = x3 >> 41; + z[0] = x0 ^ t; + z[1] = x1 ^ (t << 10); + z[2] = x2; + z[3] = x3 & M41; + } + + public static void Reduce23(ulong[] z, int zOff) + { + ulong z3 = z[zOff + 3], t = z3 >> 41; + z[zOff ] ^= t; + z[zOff + 1] ^= (t << 10); + z[zOff + 3] = z3 & M41; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7]; + zz[0] = z0 ^ (z1 << 59); + zz[1] = (z1 >> 5) ^ (z2 << 54); + zz[2] = (z2 >> 10) ^ (z3 << 49); + zz[3] = (z3 >> 15) ^ (z4 << 44); + zz[4] = (z4 >> 20) ^ (z5 << 39); + zz[5] = (z5 >> 25) ^ (z6 << 34); + zz[6] = (z6 >> 30) ^ (z7 << 29); + zz[7] = (z7 >> 35); + } + + protected static void ImplExpand(ulong[] x, ulong[] z) + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = x0 & M59; + z[1] = ((x0 >> 59) ^ (x1 << 5)) & M59; + z[2] = ((x1 >> 54) ^ (x2 << 10)) & M59; + z[3] = ((x2 >> 49) ^ (x3 << 15)); + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + ulong[] f = new ulong[4], g = new ulong[4]; + ImplExpand(x, f); + ImplExpand(y, g); + + ImplMulwAcc(f[0], g[0], zz, 0); + ImplMulwAcc(f[1], g[1], zz, 1); + ImplMulwAcc(f[2], g[2], zz, 2); + ImplMulwAcc(f[3], g[3], zz, 3); + + // U *= (1 - t^n) + for (int i = 5; i > 0; --i) + { + zz[i] ^= zz[i - 1]; + } + + ImplMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1); + ImplMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3); + + // V *= (1 - t^2n) + for (int i = 7; i > 1; --i) + { + zz[i] ^= zz[i - 2]; + } + + // Double-length recursion + { + ulong c0 = f[0] ^ f[2], c1 = f[1] ^ f[3]; + ulong d0 = g[0] ^ g[2], d1 = g[1] ^ g[3]; + ImplMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3); + ulong[] t = new ulong[3]; + ImplMulwAcc(c0, d0, t, 0); + ImplMulwAcc(c1, d1, t, 1); + ulong t0 = t[0], t1 = t[1], t2 = t[2]; + zz[2] ^= t0; + zz[3] ^= t0 ^ t1; + zz[4] ^= t2 ^ t1; + zz[5] ^= t2; + } + + ImplCompactExt(zz); + } + + protected static void ImplMulwAcc(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 59 == 0); + Debug.Assert(y >> 59 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7] + ^ (u[(j >> 3) & 7] << 3); + int k = 54; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 6) > 0); + + Debug.Assert(h >> 53 == 0); + + z[zOff ] ^= l & M59; + z[zOff + 1] ^= (l >> 59) ^ (h << 5); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + Interleave.Expand64To128(x[0], zz, 0); + Interleave.Expand64To128(x[1], zz, 2); + Interleave.Expand64To128(x[2], zz, 4); + + ulong x3 = x[3]; + zz[6] = Interleave.Expand32to64((uint)x3); + zz[7] = Interleave.Expand16to32((uint)(x3 >> 32)); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs new file mode 100644
index 000000000..a9041efde --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT233FieldElement + : ECFieldElement + { + protected readonly ulong[] x; + + public SecT233FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 233) + throw new ArgumentException("value invalid for SecT233FieldElement", "x"); + + this.x = SecT233Field.FromBigInteger(x); + } + + public SecT233FieldElement() + { + this.x = Nat256.Create64(); + } + + protected internal SecT233FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat256.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat256.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1UL) != 0UL; + } + + public override BigInteger ToBigInteger() + { + return Nat256.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT233Field"; } + } + + public override int FieldSize + { + get { return 233; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat256.Create64(); + SecT233Field.Add(x, ((SecT233FieldElement)b).x, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat256.Create64(); + SecT233Field.AddOne(x, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and Subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat256.Create64(); + SecT233Field.Multiply(x, ((SecT233FieldElement)b).x, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT233FieldElement)b).x; + ulong[] xx = ((SecT233FieldElement)x).x, yx = ((SecT233FieldElement)y).x; + + ulong[] tt = Nat256.CreateExt64(); + SecT233Field.MultiplyAddToExt(ax, bx, tt); + SecT233Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat256.Create64(); + SecT233Field.Reduce(tt, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat256.Create64(); + SecT233Field.Square(x, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT233FieldElement)x).x, yx = ((SecT233FieldElement)y).x; + + ulong[] tt = Nat256.CreateExt64(); + SecT233Field.SquareAddToExt(ax, tt); + SecT233Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat256.Create64(); + SecT233Field.Reduce(tt, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat256.Create64(); + SecT233Field.SquareN(x, pow, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat256.Create64(); + SecT233Field.Invert(x, z); + return new SecT233FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Tpb; } + } + + public virtual int M + { + get { return 233; } + } + + public virtual int K1 + { + get { return 74; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT233FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT233FieldElement); + } + + public virtual bool Equals(SecT233FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat256.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 2330074 ^ Arrays.GetHashCode(x, 0, 4); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs new file mode 100644
index 000000000..72935913d --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT233K1Curve + : AbstractF2mCurve + { + private const int SecT233K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT233K1Point m_infinity; + + public SecT233K1Curve() + : base(233, 74, 0, 0) + { + this.m_infinity = new SecT233K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.One); + this.m_order = new BigInteger(1, Hex.Decode("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF")); + this.m_cofactor = BigInteger.ValueOf(4); + + this.m_coord = SecT233K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT233K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected override ECMultiplier CreateDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public override int FieldSize + { + get { return 233; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT233FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT233K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT233K1Point(this, x, y, zs, withCompression); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override bool IsKoblitz + { + get { return true; } + } + + public virtual int M + { + get { return 233; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 74; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT233K1Point.cs b/crypto/src/math/ec/custom/sec/SecT233K1Point.cs new file mode 100644
index 000000000..7e7ee8f0b --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT233K1Point.cs
@@ -0,0 +1,302 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT233K1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT233K1Point(null, this.AffineXCoord, this.AffineYCoord); // earlier JDK + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + { + return curve.Infinity; + } + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1); + if (X3.IsZero) + { + //return new SecT233K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT233K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT233K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT233K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT233K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.Square().Add(L1); + } + else + { + T = L1.Add(Z1).Multiply(L1); + } + + if (T.IsZero) + { + //return new SecT233K1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT233K1Point(curve, T, curve.B, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.Square(); + ECFieldElement L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3).Add(Z3); + + return new SecT233K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + // NOTE: TwicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = L1Sq.Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2plus1.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + { + return b.Twice(); + } + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT233K1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT233K1Point(curve, A, curve.B, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT233K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT233K1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs new file mode 100644
index 000000000..db6e6e1d4 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT233R1Curve + : AbstractF2mCurve + { + private const int SecT233R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT233R1Point m_infinity; + + public SecT233R1Curve() + : base(233, 74, 0, 0) + { + this.m_infinity = new SecT233R1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.One); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD"))); + this.m_order = new BigInteger(1, Hex.Decode("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT233R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT233R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 233; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT233FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT233R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT233R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 233; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 74; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT233R1Point.cs b/crypto/src/math/ec/custom/sec/SecT233R1Point.cs new file mode 100644
index 000000000..ffac89d15 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT233R1Point.cs
@@ -0,0 +1,282 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT233R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT233R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + return new SecT233R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT233R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT233R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T = L1.Square().Add(L1Z1).Add(Z1Sq); + if (T.IsZero) + { + return new SecT233R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT233R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = Z1Sq.Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT233R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT233R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT233R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT239Field.cs b/crypto/src/math/ec/custom/sec/SecT239Field.cs new file mode 100644
index 000000000..6b8ad696f --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT239Field.cs
@@ -0,0 +1,286 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT239Field + { + private const ulong M47 = ulong.MaxValue >> 17; + private const ulong M60 = ulong.MaxValue >> 4; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + zz[7] = xx[7] ^ yy[7]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat256.FromBigInteger64(x); + Reduce17(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat256.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion + + ulong[] t0 = Nat256.Create64(); + ulong[] t1 = Nat256.Create64(); + + Square(x, t0); + Multiply(t0, x, t0); + Square(t0, t0); + Multiply(t0, x, t0); + SquareN(t0, 3, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 7, t0); + Multiply(t0, t1, t0); + SquareN(t0, 14, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 29, t0); + Multiply(t0, t1, t0); + Square(t0, t0); + Multiply(t0, x, t0); + SquareN(t0, 59, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 119, t0); + Multiply(t0, t1, t0); + Square(t0, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat256.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat256.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + ulong x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7]; + + x3 ^= (x7 << 17); + x4 ^= (x7 >> 47); + x5 ^= (x7 << 47); + x6 ^= (x7 >> 17); + + x2 ^= (x6 << 17); + x3 ^= (x6 >> 47); + x4 ^= (x6 << 47); + x5 ^= (x6 >> 17); + + x1 ^= (x5 << 17); + x2 ^= (x5 >> 47); + x3 ^= (x5 << 47); + x4 ^= (x5 >> 17); + + x0 ^= (x4 << 17); + x1 ^= (x4 >> 47); + x2 ^= (x4 << 47); + x3 ^= (x4 >> 17); + + ulong t = x3 >> 47; + z[0] = x0 ^ t; + z[1] = x1; + z[2] = x2 ^ (t << 30); + z[3] = x3 & M47; + } + + public static void Reduce17(ulong[] z, int zOff) + { + ulong z3 = z[zOff + 3], t = z3 >> 47; + z[zOff ] ^= t; + z[zOff + 2] ^= (t << 30); + z[zOff + 3] = z3 & M47; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat256.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7]; + zz[0] = z0 ^ (z1 << 60); + zz[1] = (z1 >> 4) ^ (z2 << 56); + zz[2] = (z2 >> 8) ^ (z3 << 52); + zz[3] = (z3 >> 12) ^ (z4 << 48); + zz[4] = (z4 >> 16) ^ (z5 << 44); + zz[5] = (z5 >> 20) ^ (z6 << 40); + zz[6] = (z6 >> 24) ^ (z7 << 36); + zz[7] = (z7 >> 28); + } + + protected static void ImplExpand(ulong[] x, ulong[] z) + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = x0 & M60; + z[1] = ((x0 >> 60) ^ (x1 << 4)) & M60; + z[2] = ((x1 >> 56) ^ (x2 << 8)) & M60; + z[3] = ((x2 >> 52) ^ (x3 << 12)); + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + ulong[] f = new ulong[4], g = new ulong[4]; + ImplExpand(x, f); + ImplExpand(y, g); + + ImplMulwAcc(f[0], g[0], zz, 0); + ImplMulwAcc(f[1], g[1], zz, 1); + ImplMulwAcc(f[2], g[2], zz, 2); + ImplMulwAcc(f[3], g[3], zz, 3); + + // U *= (1 - t^n) + for (int i = 5; i > 0; --i) + { + zz[i] ^= zz[i - 1]; + } + + ImplMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1); + ImplMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3); + + // V *= (1 - t^2n) + for (int i = 7; i > 1; --i) + { + zz[i] ^= zz[i - 2]; + } + + // Double-length recursion + { + ulong c0 = f[0] ^ f[2], c1 = f[1] ^ f[3]; + ulong d0 = g[0] ^ g[2], d1 = g[1] ^ g[3]; + ImplMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3); + ulong[] t = new ulong[3]; + ImplMulwAcc(c0, d0, t, 0); + ImplMulwAcc(c1, d1, t, 1); + ulong t0 = t[0], t1 = t[1], t2 = t[2]; + zz[2] ^= t0; + zz[3] ^= t0 ^ t1; + zz[4] ^= t2 ^ t1; + zz[5] ^= t2; + } + + ImplCompactExt(zz); + } + + protected static void ImplMulwAcc(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 60 == 0); + Debug.Assert(y >> 60 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7] + ^ (u[(j >> 3) & 7] << 3); + int k = 54; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 6) > 0); + + h ^= ((x & 0x0820820820820820L) & (ulong)(((long)y << 4) >> 63)) >> 5; + + Debug.Assert(h >> 55 == 0); + + z[zOff ] ^= l & M60; + z[zOff + 1] ^= (l >> 60) ^ (h << 4); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + Interleave.Expand64To128(x[0], zz, 0); + Interleave.Expand64To128(x[1], zz, 2); + Interleave.Expand64To128(x[2], zz, 4); + + ulong x3 = x[3]; + zz[6] = Interleave.Expand32to64((uint)x3); + zz[7] = Interleave.Expand16to32((uint)(x3 >> 32)); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs new file mode 100644
index 000000000..de074c55f --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT239FieldElement + : ECFieldElement + { + protected ulong[] x; + + public SecT239FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 239) + throw new ArgumentException("value invalid for SecT239FieldElement", "x"); + + this.x = SecT239Field.FromBigInteger(x); + } + + public SecT239FieldElement() + { + this.x = Nat256.Create64(); + } + + protected internal SecT239FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat256.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat256.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1L) != 0L; + } + + public override BigInteger ToBigInteger() + { + return Nat256.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT239Field"; } + } + + public override int FieldSize + { + get { return 239; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat256.Create64(); + SecT239Field.Add(x, ((SecT239FieldElement)b).x, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat256.Create64(); + SecT239Field.AddOne(x, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and Subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat256.Create64(); + SecT239Field.Multiply(x, ((SecT239FieldElement)b).x, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT239FieldElement)b).x; + ulong[] xx = ((SecT239FieldElement)x).x, yx = ((SecT239FieldElement)y).x; + + ulong[] tt = Nat256.CreateExt64(); + SecT239Field.MultiplyAddToExt(ax, bx, tt); + SecT239Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat256.Create64(); + SecT239Field.Reduce(tt, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat256.Create64(); + SecT239Field.Square(x, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT239FieldElement)x).x, yx = ((SecT239FieldElement)y).x; + + ulong[] tt = Nat256.CreateExt64(); + SecT239Field.SquareAddToExt(ax, tt); + SecT239Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat256.Create64(); + SecT239Field.Reduce(tt, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat256.Create64(); + SecT239Field.SquareN(x, pow, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat256.Create64(); + SecT239Field.Invert(x, z); + return new SecT239FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Tpb; } + } + + public virtual int M + { + get { return 239; } + } + + public virtual int K1 + { + get { return 158; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT239FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT239FieldElement); + } + + public virtual bool Equals(SecT239FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat256.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 23900158 ^ Arrays.GetHashCode(x, 0, 4); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs new file mode 100644
index 000000000..a499d48b4 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT239K1Curve + : AbstractF2mCurve + { + private const int SecT239K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT239K1Point m_infinity; + + public SecT239K1Curve() + : base(239, 158, 0, 0) + { + this.m_infinity = new SecT239K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.One); + this.m_order = new BigInteger(1, Hex.Decode("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5")); + this.m_cofactor = BigInteger.ValueOf(4); + + this.m_coord = SecT239K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT239K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected override ECMultiplier CreateDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 239; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT239FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT239K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT239K1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return true; } + } + + public virtual int M + { + get { return 239; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 158; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT239K1Point.cs b/crypto/src/math/ec/custom/sec/SecT239K1Point.cs new file mode 100644
index 000000000..ac079ad1e --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT239K1Point.cs
@@ -0,0 +1,297 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT239K1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT239K1Point(null, this.AffineXCoord, this.AffineYCoord); // earlier JDK + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + // X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1); + if (X3.IsZero) + { + //return new SecT239K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT239K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT239K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT239K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT239K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.Square().Add(L1); + } + else + { + T = L1.Add(Z1).Multiply(L1); + } + + if (T.IsZero) + { + //return new SecT239K1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT239K1Point(curve, T, curve.B, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.Square(); + ECFieldElement L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3).Add(Z3); + + return new SecT239K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + // NOTE: TwicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = L1Sq.Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2plus1.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT239K1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT239K1Point(curve, A, curve.B, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT239K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT239K1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT283Field.cs b/crypto/src/math/ec/custom/sec/SecT283Field.cs new file mode 100644
index 000000000..903ea02ff --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT283Field.cs
@@ -0,0 +1,370 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT283Field + { + private const ulong M27 = ulong.MaxValue >> 37; + private const ulong M57 = ulong.MaxValue >> 7; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + z[4] = x[4] ^ y[4]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + zz[7] = xx[7] ^ yy[7]; + zz[8] = xx[8] ^ yy[8]; + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat320.FromBigInteger64(x); + Reduce37(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat320.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion + + ulong[] t0 = Nat320.Create64(); + ulong[] t1 = Nat320.Create64(); + + Square(x, t0); + Multiply(t0, x, t0); + SquareN(t0, 2, t1); + Multiply(t1, t0, t1); + SquareN(t1, 4, t0); + Multiply(t0, t1, t0); + SquareN(t0, 8, t1); + Multiply(t1, t0, t1); + Square(t1, t1); + Multiply(t1, x, t1); + SquareN(t1, 17, t0); + Multiply(t0, t1, t0); + Square(t0, t0); + Multiply(t0, x, t0); + SquareN(t0, 35, t1); + Multiply(t1, t0, t1); + SquareN(t1, 70, t0); + Multiply(t0, t1, t0); + Square(t0, t0); + Multiply(t0, x, t0); + SquareN(t0, 141, t1); + Multiply(t1, t0, t1); + Square(t1, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat320.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat320.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4]; + ulong x5 = xx[5], x6 = xx[6], x7 = xx[7], x8 = xx[8]; + + x3 ^= (x8 << 37) ^ (x8 << 42) ^ (x8 << 44) ^ (x8 << 49); + x4 ^= (x8 >> 27) ^ (x8 >> 22) ^ (x8 >> 20) ^ (x8 >> 15); + + x2 ^= (x7 << 37) ^ (x7 << 42) ^ (x7 << 44) ^ (x7 << 49); + x3 ^= (x7 >> 27) ^ (x7 >> 22) ^ (x7 >> 20) ^ (x7 >> 15); + + x1 ^= (x6 << 37) ^ (x6 << 42) ^ (x6 << 44) ^ (x6 << 49); + x2 ^= (x6 >> 27) ^ (x6 >> 22) ^ (x6 >> 20) ^ (x6 >> 15); + + x0 ^= (x5 << 37) ^ (x5 << 42) ^ (x5 << 44) ^ (x5 << 49); + x1 ^= (x5 >> 27) ^ (x5 >> 22) ^ (x5 >> 20) ^ (x5 >> 15); + + ulong t = x4 >> 27; + z[0] = x0 ^ t ^ (t << 5) ^ (t << 7) ^ (t << 12); + z[1] = x1; + z[2] = x2; + z[3] = x3; + z[4] = x4 & M27; + } + + public static void Reduce37(ulong[] z, int zOff) + { + ulong z4 = z[zOff + 4], t = z4 >> 27; + z[zOff ] ^= t ^ (t << 5) ^ (t << 7) ^ (t << 12); + z[zOff + 4] = z4 & M27; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat.Create64(9); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat.Create64(9); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat.Create64(9); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4]; + ulong z5 = zz[5], z6 = zz[6], z7 = zz[7], z8 = zz[8], z9 = zz[9]; + zz[0] = z0 ^ (z1 << 57); + zz[1] = (z1 >> 7) ^ (z2 << 50); + zz[2] = (z2 >> 14) ^ (z3 << 43); + zz[3] = (z3 >> 21) ^ (z4 << 36); + zz[4] = (z4 >> 28) ^ (z5 << 29); + zz[5] = (z5 >> 35) ^ (z6 << 22); + zz[6] = (z6 >> 42) ^ (z7 << 15); + zz[7] = (z7 >> 49) ^ (z8 << 8); + zz[8] = (z8 >> 56) ^ (z9 << 1); + zz[9] = (z9 >> 63); // Zero! + } + + protected static void ImplExpand(ulong[] x, ulong[] z) + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4]; + z[0] = x0 & M57; + z[1] = ((x0 >> 57) ^ (x1 << 7)) & M57; + z[2] = ((x1 >> 50) ^ (x2 << 14)) & M57; + z[3] = ((x2 >> 43) ^ (x3 << 21)) & M57; + z[4] = ((x3 >> 36) ^ (x4 << 28)); + } + + //protected static void AddMs(ulong[] zz, int zOff, ulong[] p, params int[] ms) + //{ + // ulong t0 = 0, t1 = 0; + // foreach (int m in ms) + // { + // int i = (m - 1) << 1; + // t0 ^= p[i ]; + // t1 ^= p[i + 1]; + // } + // zz[zOff ] ^= t0; + // zz[zOff + 1] ^= t1; + //} + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + /* + * Formula (17) from "Some New Results on Binary Polynomial Multiplication", + * Murat Cenk and M. Anwar Hasan. + * + * The formula as given contained an error in the term t25, as noted below + */ + ulong[] a = new ulong[5], b = new ulong[5]; + ImplExpand(x, a); + ImplExpand(y, b); + + ulong[] p = new ulong[26]; + + ImplMulw(a[0], b[0], p, 0); // m1 + ImplMulw(a[1], b[1], p, 2); // m2 + ImplMulw(a[2], b[2], p, 4); // m3 + ImplMulw(a[3], b[3], p, 6); // m4 + ImplMulw(a[4], b[4], p, 8); // m5 + + ulong u0 = a[0] ^ a[1], v0 = b[0] ^ b[1]; + ulong u1 = a[0] ^ a[2], v1 = b[0] ^ b[2]; + ulong u2 = a[2] ^ a[4], v2 = b[2] ^ b[4]; + ulong u3 = a[3] ^ a[4], v3 = b[3] ^ b[4]; + + ImplMulw(u1 ^ a[3], v1 ^ b[3], p, 18); // m10 + ImplMulw(u2 ^ a[1], v2 ^ b[1], p, 20); // m11 + + ulong A4 = u0 ^ u3 , B4 = v0 ^ v3; + ulong A5 = A4 ^ a[2], B5 = B4 ^ b[2]; + + ImplMulw(A4, B4, p, 22); // m12 + ImplMulw(A5, B5, p, 24); // m13 + + ImplMulw(u0, v0, p, 10); // m6 + ImplMulw(u1, v1, p, 12); // m7 + ImplMulw(u2, v2, p, 14); // m8 + ImplMulw(u3, v3, p, 16); // m9 + + + // Original method, corresponding to formula (16) + //AddMs(zz, 0, p, 1); + //AddMs(zz, 1, p, 1, 2, 6); + //AddMs(zz, 2, p, 1, 2, 3, 7); + //AddMs(zz, 3, p, 1, 3, 4, 5, 8, 10, 12, 13); + //AddMs(zz, 4, p, 1, 2, 4, 5, 6, 9, 10, 11, 13); + //AddMs(zz, 5, p, 1, 2, 3, 5, 7, 11, 12, 13); + //AddMs(zz, 6, p, 3, 4, 5, 8); + //AddMs(zz, 7, p, 4, 5, 9); + //AddMs(zz, 8, p, 5); + + // Improved method factors out common single-word terms + // NOTE: p1,...,p26 in the paper maps to p[0],...,p[25] here + + zz[0] = p[ 0]; + zz[9] = p[ 9]; + + ulong t1 = p[ 0] ^ p[ 1]; + ulong t2 = t1 ^ p[ 2]; + ulong t3 = t2 ^ p[10]; + + zz[1] = t3; + + ulong t4 = p[ 3] ^ p[ 4]; + ulong t5 = p[11] ^ p[12]; + ulong t6 = t4 ^ t5; + ulong t7 = t2 ^ t6; + + zz[2] = t7; + + ulong t8 = t1 ^ t4; + ulong t9 = p[ 5] ^ p[ 6]; + ulong t10 = t8 ^ t9; + ulong t11 = t10 ^ p[ 8]; + ulong t12 = p[13] ^ p[14]; + ulong t13 = t11 ^ t12; + ulong t14 = p[18] ^ p[22]; + ulong t15 = t14 ^ p[24]; + ulong t16 = t13 ^ t15; + + zz[3] = t16; + + ulong t17 = p[ 7] ^ p[ 8]; + ulong t18 = t17 ^ p[ 9]; + ulong t19 = t18 ^ p[17]; + + zz[8] = t19; + + ulong t20 = t18 ^ t9; + ulong t21 = p[15] ^ p[16]; + ulong t22 = t20 ^ t21; + + zz[7] = t22; + + ulong t23 = t22 ^ t3; + ulong t24 = p[19] ^ p[20]; + // ulong t25 = p[23] ^ p[24]; + ulong t25 = p[25] ^ p[24]; // Fixes an error in the paper: p[23] -> p{25] + ulong t26 = p[18] ^ p[23]; + ulong t27 = t24 ^ t25; + ulong t28 = t27 ^ t26; + ulong t29 = t28 ^ t23; + + zz[4] = t29; + + ulong t30 = t7 ^ t19; + ulong t31 = t27 ^ t30; + ulong t32 = p[21] ^ p[22]; + ulong t33 = t31 ^ t32; + + zz[5] = t33; + + ulong t34 = t11 ^ p[0]; + ulong t35 = t34 ^ p[9]; + ulong t36 = t35 ^ t12; + ulong t37 = t36 ^ p[21]; + ulong t38 = t37 ^ p[23]; + ulong t39 = t38 ^ p[25]; + + zz[6] = t39; + + ImplCompactExt(zz); + } + + protected static void ImplMulw(ulong x, ulong y, ulong[] z, int zOff) + { + Debug.Assert(x >> 57 == 0); + Debug.Assert(y >> 57 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7]; + int k = 48; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3 + ^ u[(j >> 6) & 7] << 6; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 9) > 0); + + h ^= ((x & 0x0100804020100800L) & (ulong)(((long)y << 7) >> 63)) >> 8; + + Debug.Assert(h >> 49 == 0); + + z[zOff ] = l & M57; + z[zOff + 1] = (l >> 57) ^ (h << 7); + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + for (int i = 0; i < 4; ++i) + { + Interleave.Expand64To128(x[i], zz, i << 1); + } + zz[8] = Interleave.Expand32to64((uint)x[4]); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs new file mode 100644
index 000000000..e02108f73 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT283FieldElement + : ECFieldElement + { + protected readonly ulong[] x; + + public SecT283FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 283) + throw new ArgumentException("value invalid for SecT283FieldElement", "x"); + + this.x = SecT283Field.FromBigInteger(x); + } + + public SecT283FieldElement() + { + this.x = Nat320.Create64(); + } + + protected internal SecT283FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat320.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat320.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1UL) != 0UL; + } + + public override BigInteger ToBigInteger() + { + return Nat320.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT283Field"; } + } + + public override int FieldSize + { + get { return 283; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat320.Create64(); + SecT283Field.Add(x, ((SecT283FieldElement)b).x, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat320.Create64(); + SecT283Field.AddOne(x, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat320.Create64(); + SecT283Field.Multiply(x, ((SecT283FieldElement)b).x, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT283FieldElement)b).x; + ulong[] xx = ((SecT283FieldElement)x).x, yx = ((SecT283FieldElement)y).x; + + ulong[] tt = Nat.Create64(9); + SecT283Field.MultiplyAddToExt(ax, bx, tt); + SecT283Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat320.Create64(); + SecT283Field.Reduce(tt, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat320.Create64(); + SecT283Field.Square(x, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT283FieldElement)x).x, yx = ((SecT283FieldElement)y).x; + + ulong[] tt = Nat.Create64(9); + SecT283Field.SquareAddToExt(ax, tt); + SecT283Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat320.Create64(); + SecT283Field.Reduce(tt, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat320.Create64(); + SecT283Field.SquareN(x, pow, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat320.Create64(); + SecT283Field.Invert(x, z); + return new SecT283FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Ppb; } + } + + public virtual int M + { + get { return 283; } + } + + public virtual int K1 + { + get { return 5; } + } + + public virtual int K2 + { + get { return 7; } + } + + public virtual int K3 + { + get { return 12; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT283FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT283FieldElement); + } + + public virtual bool Equals(SecT283FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat320.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 2831275 ^ Arrays.GetHashCode(x, 0, 5); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs new file mode 100644
index 000000000..4053287ec --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT283K1Curve + : AbstractF2mCurve + { + private const int SecT283K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT283K1Point m_infinity; + + public SecT283K1Curve() + : base(283, 5, 7, 12) + { + this.m_infinity = new SecT283K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.One); + this.m_order = new BigInteger(1, Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61")); + this.m_cofactor = BigInteger.ValueOf(4); + + this.m_coord = SecT283K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT283K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected override ECMultiplier CreateDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 283; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT283FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT283K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT283K1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return true; } + } + + public virtual int M + { + get { return 283; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 5; } + } + + public virtual int K2 + { + get { return 7; } + } + + public virtual int K3 + { + get { return 12; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT283K1Point.cs b/crypto/src/math/ec/custom/sec/SecT283K1Point.cs new file mode 100644
index 000000000..f85706c63 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT283K1Point.cs
@@ -0,0 +1,296 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT283K1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT283K1Point(null, this.AffineXCoord, this.AffineYCoord); // earlier JDK + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1); + if (X3.IsZero) + { + //return new SecT283K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT283K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT283K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT283K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT283K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.Square().Add(L1); + } + else + { + T = L1.Add(Z1).Multiply(L1); + } + + if (T.IsZero) + { + //return new SecT283K1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT283K1Point(curve, T, curve.B, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.Square(); + ECFieldElement L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3).Add(Z3); + + return new SecT283K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + // NOTE: TwicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = L1Sq.Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2plus1.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT283K1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT283K1Point(curve, A, curve.B, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT283K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT283K1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs new file mode 100644
index 000000000..e659675ce --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT283R1Curve + : AbstractF2mCurve + { + private const int SecT283R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT283R1Point m_infinity; + + public SecT283R1Curve() + : base(283, 5, 7, 12) + { + this.m_infinity = new SecT283R1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.One); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5"))); + this.m_order = new BigInteger(1, Hex.Decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT283R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT283R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 283; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT283FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT283R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT283R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 283; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 5; } + } + + public virtual int K2 + { + get { return 7; } + } + + public virtual int K3 + { + get { return 12; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT283R1Point.cs b/crypto/src/math/ec/custom/sec/SecT283R1Point.cs new file mode 100644
index 000000000..340bbdae6 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT283R1Point.cs
@@ -0,0 +1,282 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT283R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT283R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + return new SecT283R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT283R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT283R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T = L1.Square().Add(L1Z1).Add(Z1Sq); + if (T.IsZero) + { + return new SecT283R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT283R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = Z1Sq.Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT283R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT283R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT283R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT409Field.cs b/crypto/src/math/ec/custom/sec/SecT409Field.cs new file mode 100644
index 000000000..84eada96e --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT409Field.cs
@@ -0,0 +1,295 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT409Field + { + private const ulong M25 = ulong.MaxValue >> 39; + private const ulong M59 = ulong.MaxValue >> 5; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + z[4] = x[4] ^ y[4]; + z[5] = x[5] ^ y[5]; + z[6] = x[6] ^ y[6]; + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + for (int i = 0; i < 13; ++i) + { + zz[i] = xx[i] ^ yy[i]; + } + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + z[5] = x[5]; + z[6] = x[6]; + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat448.FromBigInteger64(x); + Reduce39(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat448.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion with bases { 2, 3 } + + ulong[] t0 = Nat448.Create64(); + ulong[] t1 = Nat448.Create64(); + ulong[] t2 = Nat448.Create64(); + + Square(x, t0); + + // 3 | 408 + SquareN(t0, 1, t1); + Multiply(t0, t1, t0); + SquareN(t1, 1, t1); + Multiply(t0, t1, t0); + + // 2 | 136 + SquareN(t0, 3, t1); + Multiply(t0, t1, t0); + + // 2 | 68 + SquareN(t0, 6, t1); + Multiply(t0, t1, t0); + + // 2 | 34 + SquareN(t0, 12, t1); + Multiply(t0, t1, t2); + + // ! {2,3} | 17 + SquareN(t2, 24, t0); + SquareN(t0, 24, t1); + Multiply(t0, t1, t0); + + // 2 | 8 + SquareN(t0, 48, t1); + Multiply(t0, t1, t0); + + // 2 | 4 + SquareN(t0, 96, t1); + Multiply(t0, t1, t0); + + // 2 | 2 + SquareN(t0, 192, t1); + Multiply(t0, t1, t0); + + Multiply(t0, t2, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat448.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat448.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong x00 = xx[0], x01 = xx[1], x02 = xx[2], x03 = xx[3]; + ulong x04 = xx[4], x05 = xx[5], x06 = xx[6], x07 = xx[7]; + + ulong u = xx[12]; + x05 ^= (u << 39); + x06 ^= (u >> 25) ^ (u << 62); + x07 ^= (u >> 2); + + u = xx[11]; + x04 ^= (u << 39); + x05 ^= (u >> 25) ^ (u << 62); + x06 ^= (u >> 2); + + u = xx[10]; + x03 ^= (u << 39); + x04 ^= (u >> 25) ^ (u << 62); + x05 ^= (u >> 2); + + u = xx[9]; + x02 ^= (u << 39); + x03 ^= (u >> 25) ^ (u << 62); + x04 ^= (u >> 2); + + u = xx[8]; + x01 ^= (u << 39); + x02 ^= (u >> 25) ^ (u << 62); + x03 ^= (u >> 2); + + u = x07; + x00 ^= (u << 39); + x01 ^= (u >> 25) ^ (u << 62); + x02 ^= (u >> 2); + + ulong t = x06 >> 25; + z[0] = x00 ^ t; + z[1] = x01 ^ (t << 23); + z[2] = x02; + z[3] = x03; + z[4] = x04; + z[5] = x05; + z[6] = x06 & M25; + } + + public static void Reduce39(ulong[] z, int zOff) + { + ulong z6 = z[zOff + 6], t = z6 >> 25; + z[zOff ] ^= t; + z[zOff + 1] ^= (t << 23); + z[zOff + 6] = z6 & M25; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat.Create64(13); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat.Create64(13); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat.Create64(13); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplCompactExt(ulong[] zz) + { + ulong z00 = zz[ 0], z01 = zz[ 1], z02 = zz[ 2], z03 = zz[ 3], z04 = zz[ 4], z05 = zz[ 5], z06 = zz[ 6]; + ulong z07 = zz[ 7], z08 = zz[ 8], z09 = zz[ 9], z10 = zz[10], z11 = zz[11], z12 = zz[12], z13 = zz[13]; + zz[ 0] = z00 ^ (z01 << 59); + zz[ 1] = (z01 >> 5) ^ (z02 << 54); + zz[ 2] = (z02 >> 10) ^ (z03 << 49); + zz[ 3] = (z03 >> 15) ^ (z04 << 44); + zz[ 4] = (z04 >> 20) ^ (z05 << 39); + zz[ 5] = (z05 >> 25) ^ (z06 << 34); + zz[ 6] = (z06 >> 30) ^ (z07 << 29); + zz[ 7] = (z07 >> 35) ^ (z08 << 24); + zz[ 8] = (z08 >> 40) ^ (z09 << 19); + zz[ 9] = (z09 >> 45) ^ (z10 << 14); + zz[10] = (z10 >> 50) ^ (z11 << 9); + zz[11] = (z11 >> 55) ^ (z12 << 4) + ^ (z13 << 63); + zz[12] = (z12 >> 60) + ^ (z13 >> 1); + zz[13] = 0; + } + + protected static void ImplExpand(ulong[] x, ulong[] z) + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6]; + z[0] = x0 & M59; + z[1] = ((x0 >> 59) ^ (x1 << 5)) & M59; + z[2] = ((x1 >> 54) ^ (x2 << 10)) & M59; + z[3] = ((x2 >> 49) ^ (x3 << 15)) & M59; + z[4] = ((x3 >> 44) ^ (x4 << 20)) & M59; + z[5] = ((x4 >> 39) ^ (x5 << 25)) & M59; + z[6] = ((x5 >> 34) ^ (x6 << 30)); + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] a = new ulong[7], b = new ulong[7]; + ImplExpand(x, a); + ImplExpand(y, b); + + for (int i = 0; i < 7; ++i) + { + ImplMulwAcc(a, b[i], zz, i); + } + + ImplCompactExt(zz); + } + + protected static void ImplMulwAcc(ulong[] xs, ulong y, ulong[] z, int zOff) + { + Debug.Assert(y >> 59 == 0); + + ulong[] u = new ulong[8]; + //u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + for (int i = 0; i < 7; ++i) + { + ulong x = xs[i]; + + Debug.Assert(x >> 59 == 0); + + uint j = (uint)x; + ulong g, h = 0, l = u[j & 7] + ^ (u[(j >> 3) & 7] << 3); + int k = 54; + do + { + j = (uint)(x >> k); + g = u[j & 7] + ^ u[(j >> 3) & 7] << 3; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 6) > 0); + + Debug.Assert(h >> 53 == 0); + + z[zOff + i ] ^= l & M59; + z[zOff + i + 1] ^= (l >> 59) ^ (h << 5); + } + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + for (int i = 0; i < 6; ++i) + { + Interleave.Expand64To128(x[i], zz, i << 1); + } + zz[12] = Interleave.Expand32to64((uint)x[6]); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs new file mode 100644
index 000000000..581ea73df --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT409FieldElement + : ECFieldElement + { + protected ulong[] x; + + public SecT409FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 409) + throw new ArgumentException("value invalid for SecT409FieldElement", "x"); + + this.x = SecT409Field.FromBigInteger(x); + } + + public SecT409FieldElement() + { + this.x = Nat448.Create64(); + } + + protected internal SecT409FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat448.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat448.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1UL) != 0UL; + } + + public override BigInteger ToBigInteger() + { + return Nat448.ToBigInteger64(x); + } + + public override string FieldName + { + get { return "SecT409Field"; } + } + + public override int FieldSize + { + get { return 409; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat448.Create64(); + SecT409Field.Add(x, ((SecT409FieldElement)b).x, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat448.Create64(); + SecT409Field.AddOne(x, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat448.Create64(); + SecT409Field.Multiply(x, ((SecT409FieldElement)b).x, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT409FieldElement)b).x; + ulong[] xx = ((SecT409FieldElement)x).x, yx = ((SecT409FieldElement)y).x; + + ulong[] tt = Nat.Create64(13); + SecT409Field.MultiplyAddToExt(ax, bx, tt); + SecT409Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat448.Create64(); + SecT409Field.Reduce(tt, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat448.Create64(); + SecT409Field.Square(x, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT409FieldElement)x).x, yx = ((SecT409FieldElement)y).x; + + ulong[] tt = Nat.Create64(13); + SecT409Field.SquareAddToExt(ax, tt); + SecT409Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat448.Create64(); + SecT409Field.Reduce(tt, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat448.Create64(); + SecT409Field.SquareN(x, pow, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat448.Create64(); + SecT409Field.Invert(x, z); + return new SecT409FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Tpb; } + } + + public virtual int M + { + get { return 409; } + } + + public virtual int K1 + { + get { return 87; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT409FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT409FieldElement); + } + + public virtual bool Equals(SecT409FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat448.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 4090087 ^ Arrays.GetHashCode(x, 0, 7); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs new file mode 100644
index 000000000..4f573553e --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT409K1Curve + : AbstractF2mCurve + { + private const int SecT409K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT409K1Point m_infinity; + + public SecT409K1Curve() + : base(409, 87, 0, 0) + { + this.m_infinity = new SecT409K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.One); + this.m_order = new BigInteger(1, Hex.Decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF")); + this.m_cofactor = BigInteger.ValueOf(4); + + this.m_coord = SecT409K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT409K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected override ECMultiplier CreateDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 409; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT409FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT409K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT409K1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return true; } + } + + public virtual int M + { + get { return 409; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 87; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT409K1Point.cs b/crypto/src/math/ec/custom/sec/SecT409K1Point.cs new file mode 100644
index 000000000..71adc7af2 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT409K1Point.cs
@@ -0,0 +1,296 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT409K1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT409K1Point(null, this.AffineXCoord, this.AffineYCoord); // earlier JDK + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1); + if (X3.IsZero) + { + //return new SecT409K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT409K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT409K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT409K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT409K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.Square().Add(L1); + } + else + { + T = L1.Add(Z1).Multiply(L1); + } + + if (T.IsZero) + { + //return new SecT409K1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT409K1Point(curve, T, curve.B, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.Square(); + ECFieldElement L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3).Add(Z3); + + return new SecT409K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + // NOTE: TwicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = L1Sq.Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2plus1.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT409K1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT409K1Point(curve, A, curve.B, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT409K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT409K1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs new file mode 100644
index 000000000..9212fb5d2 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT409R1Curve + : AbstractF2mCurve + { + private const int SecT409R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT409R1Point m_infinity; + + public SecT409R1Curve() + : base(409, 87, 0, 0) + { + this.m_infinity = new SecT409R1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.One); + this.m_b = FromBigInteger(new BigInteger(1, Hex.Decode("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F"))); + this.m_order = new BigInteger(1, Hex.Decode("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT409R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT409R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 409; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT409FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT409R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT409R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 409; } + } + + public virtual bool IsTrinomial + { + get { return true; } + } + + public virtual int K1 + { + get { return 87; } + } + + public virtual int K2 + { + get { return 0; } + } + + public virtual int K3 + { + get { return 0; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT409R1Point.cs b/crypto/src/math/ec/custom/sec/SecT409R1Point.cs new file mode 100644
index 000000000..af69fe656 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT409R1Point.cs
@@ -0,0 +1,282 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT409R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT409R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + return new SecT409R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new SecT409R1Point(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT409R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T = L1.Square().Add(L1Z1).Add(Z1Sq); + if (T.IsZero) + { + return new SecT409R1Point(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT409R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = Z1Sq.Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + return new SecT409R1Point(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT409R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT409R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT571Field.cs b/crypto/src/math/ec/custom/sec/SecT571Field.cs new file mode 100644
index 000000000..fc84e336b --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT571Field.cs
@@ -0,0 +1,302 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.Raw; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT571Field + { + private const ulong M59 = ulong.MaxValue >> 5; + + private const ulong RM = 0xEF7BDEF7BDEF7BDEUL; + + public static void Add(ulong[] x, ulong[] y, ulong[] z) + { + for (int i = 0; i < 9; ++i) + { + z[i] = x[i] ^ y[i]; + } + } + + private static void Add(ulong[] x, int xOff, ulong[] y, int yOff, ulong[] z, int zOff) + { + for (int i = 0; i < 9; ++i) + { + z[zOff + i] = x[xOff + i] ^ y[yOff + i]; + } + } + + private static void AddBothTo(ulong[] x, int xOff, ulong[] y, int yOff, ulong[] z, int zOff) + { + for (int i = 0; i < 9; ++i) + { + z[zOff + i] ^= x[xOff + i] ^ y[yOff + i]; + } + } + + public static void AddExt(ulong[] xx, ulong[] yy, ulong[] zz) + { + for (int i = 0; i < 18; ++i) + { + zz[i] = xx[i] ^ yy[i]; + } + } + + public static void AddOne(ulong[] x, ulong[] z) + { + z[0] = x[0] ^ 1UL; + for (int i = 1; i < 9; ++i) + { + z[i] = x[i]; + } + } + + public static ulong[] FromBigInteger(BigInteger x) + { + ulong[] z = Nat576.FromBigInteger64(x); + Reduce5(z, 0); + return z; + } + + public static void Invert(ulong[] x, ulong[] z) + { + if (Nat576.IsZero64(x)) + throw new InvalidOperationException(); + + // Itoh-Tsujii inversion with bases { 2, 3, 5 } + + ulong[] t0 = Nat576.Create64(); + ulong[] t1 = Nat576.Create64(); + ulong[] t2 = Nat576.Create64(); + + Square(x, t2); + + // 5 | 570 + Square(t2, t0); + Square(t0, t1); + Multiply(t0, t1, t0); + SquareN(t0, 2, t1); + Multiply(t0, t1, t0); + Multiply(t0, t2, t0); + + // 3 | 114 + SquareN(t0, 5, t1); + Multiply(t0, t1, t0); + SquareN(t1, 5, t1); + Multiply(t0, t1, t0); + + // 2 | 38 + SquareN(t0, 15, t1); + Multiply(t0, t1, t2); + + // ! {2,3,5} | 19 + SquareN(t2, 30, t0); + SquareN(t0, 30, t1); + Multiply(t0, t1, t0); + + // 3 | 9 + SquareN(t0, 60, t1); + Multiply(t0, t1, t0); + SquareN(t1, 60, t1); + Multiply(t0, t1, t0); + + // 3 | 3 + SquareN(t0, 180, t1); + Multiply(t0, t1, t0); + SquareN(t1, 180, t1); + Multiply(t0, t1, t0); + + Multiply(t0, t2, z); + } + + public static void Multiply(ulong[] x, ulong[] y, ulong[] z) + { + ulong[] tt = Nat576.CreateExt64(); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(ulong[] x, ulong[] y, ulong[] zz) + { + ulong[] tt = Nat576.CreateExt64(); + ImplMultiply(x, y, tt); + AddExt(zz, tt, zz); + } + + public static void Reduce(ulong[] xx, ulong[] z) + { + ulong xx09 = xx[9]; + ulong u = xx[17], v = xx09; + + xx09 = v ^ (u >> 59) ^ (u >> 57) ^ (u >> 54) ^ (u >> 49); + v = xx[8] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15); + + for (int i = 16; i >= 10; --i) + { + u = xx[i]; + z[i - 8] = v ^ (u >> 59) ^ (u >> 57) ^ (u >> 54) ^ (u >> 49); + v = xx[i - 9] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15); + } + + u = xx09; + z[1] = v ^ (u >> 59) ^ (u >> 57) ^ (u >> 54) ^ (u >> 49); + v = xx[0] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15); + + ulong x08 = z[8]; + ulong t = x08 >> 59; + z[0] = v ^ t ^ (t << 2) ^ (t << 5) ^ (t << 10); + z[8] = x08 & M59; + } + + public static void Reduce5(ulong[] z, int zOff) + { + ulong z8 = z[zOff + 8], t = z8 >> 59; + z[zOff ] ^= t ^ (t << 2) ^ (t << 5) ^ (t << 10); + z[zOff + 8] = z8 & M59; + } + + public static void Square(ulong[] x, ulong[] z) + { + ulong[] tt = Nat576.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareAddToExt(ulong[] x, ulong[] zz) + { + ulong[] tt = Nat576.CreateExt64(); + ImplSquare(x, tt); + AddExt(zz, tt, zz); + } + + public static void SquareN(ulong[] x, int n, ulong[] z) + { + Debug.Assert(n > 0); + + ulong[] tt = Nat576.CreateExt64(); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + protected static void ImplMultiply(ulong[] x, ulong[] y, ulong[] zz) + { + //for (int i = 0; i < 9; ++i) + //{ + // ImplMulwAcc(x, y[i], zz, i); + //} + + /* + * Precompute table of all 4-bit products of y + */ + ulong[] T0 = new ulong[9 << 4]; + Array.Copy(y, 0, T0, 9, 9); + // Reduce5(T0, 9); + int tOff = 0; + for (int i = 7; i > 0; --i) + { + tOff += 18; + Nat.ShiftUpBit64(9, T0, tOff >> 1, 0UL, T0, tOff); + Reduce5(T0, tOff); + Add(T0, 9, T0, tOff, T0, tOff + 9); + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + ulong[] T1 = new ulong[T0.Length]; + Nat.ShiftUpBits64(T0.Length, T0, 0, 4, 0L, T1, 0); + + uint MASK = 0xF; + + /* + * Lopez-Dahab algorithm + */ + + for (int k = 56; k >= 0; k -= 8) + { + for (int j = 1; j < 9; j += 2) + { + uint aVal = (uint)(x[j] >> k); + uint u = aVal & MASK; + uint v = (aVal >> 4) & MASK; + AddBothTo(T0, (int)(9 * u), T1, (int)(9 * v), zz, j - 1); + } + Nat.ShiftUpBits64(16, zz, 0, 8, 0L); + } + + for (int k = 56; k >= 0; k -= 8) + { + for (int j = 0; j < 9; j += 2) + { + uint aVal = (uint)(x[j] >> k); + uint u = aVal & MASK; + uint v = (aVal >> 4) & MASK; + AddBothTo(T0, (int)(9 * u), T1, (int)(9 * v), zz, j); + } + if (k > 0) + { + Nat.ShiftUpBits64(18, zz, 0, 8, 0L); + } + } + } + + protected static void ImplMulwAcc(ulong[] xs, ulong y, ulong[] z, int zOff) + { + ulong[] u = new ulong[32]; + //u[0] = 0; + u[1] = y; + for (int i = 2; i < 32; i += 2) + { + u[i ] = u[i >> 1] << 1; + u[i + 1] = u[i ] ^ y; + } + + ulong l = 0; + for (int i = 0; i < 9; ++i) + { + ulong x = xs[i]; + + uint j = (uint)x; + + l ^= u[j & 31]; + + ulong g, h = 0; + int k = 60; + do + { + j = (uint)(x >> k); + g = u[j & 31]; + l ^= (g << k); + h ^= (g >> -k); + } + while ((k -= 5) > 0); + + for (int p = 0; p < 4; ++p) + { + x = (x & RM) >> 1; + h ^= x & (ulong)(((long)y << p) >> 63); + } + + z[zOff + i] ^= l; + + l = h; + } + z[zOff + 9] ^= l; + } + + protected static void ImplSquare(ulong[] x, ulong[] zz) + { + for (int i = 0; i < 9; ++i) + { + Interleave.Expand64To128(x[i], zz, i << 1); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs new file mode 100644
index 000000000..5d5458412 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Math.Raw; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT571FieldElement + : ECFieldElement + { + protected readonly ulong[] x; + + public SecT571FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.BitLength > 571) + throw new ArgumentException("value invalid for SecT571FieldElement", "x"); + + this.x = SecT571Field.FromBigInteger(x); + } + + public SecT571FieldElement() + { + this.x = Nat576.Create64(); + } + + protected internal SecT571FieldElement(ulong[] x) + { + this.x = x; + } + + public override bool IsOne + { + get { return Nat576.IsOne64(x); } + } + + public override bool IsZero + { + get { return Nat576.IsZero64(x); } + } + + public override bool TestBitZero() + { + return (x[0] & 1UL) != 0UL; + } + + public override BigInteger ToBigInteger() + { + return Nat576.ToBigInteger64(x); + } + + public override String FieldName + { + get { return "SecT571Field"; } + } + + public override int FieldSize + { + get { return 571; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + ulong[] z = Nat576.Create64(); + SecT571Field.Add(x, ((SecT571FieldElement)b).x, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement AddOne() + { + ulong[] z = Nat576.Create64(); + SecT571Field.AddOne(x, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + ulong[] z = Nat576.Create64(); + SecT571Field.Multiply(x, ((SecT571FieldElement)b).x, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x, bx = ((SecT571FieldElement)b).x; + ulong[] xx = ((SecT571FieldElement)x).x, yx = ((SecT571FieldElement)y).x; + + ulong[] tt = Nat576.CreateExt64(); + SecT571Field.MultiplyAddToExt(ax, bx, tt); + SecT571Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat576.Create64(); + SecT571Field.Reduce(tt, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + return Multiply(b.Invert()); + } + + public override ECFieldElement Negate() + { + return this; + } + + public override ECFieldElement Square() + { + ulong[] z = Nat576.Create64(); + SecT571Field.Square(x, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + ulong[] ax = this.x; + ulong[] xx = ((SecT571FieldElement)x).x, yx = ((SecT571FieldElement)y).x; + + ulong[] tt = Nat576.CreateExt64(); + SecT571Field.SquareAddToExt(ax, tt); + SecT571Field.MultiplyAddToExt(xx, yx, tt); + + ulong[] z = Nat576.Create64(); + SecT571Field.Reduce(tt, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement SquarePow(int pow) + { + if (pow < 1) + return this; + + ulong[] z = Nat576.Create64(); + SecT571Field.SquareN(x, pow, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement Invert() + { + ulong[] z = Nat576.Create64(); + SecT571Field.Invert(x, z); + return new SecT571FieldElement(z); + } + + public override ECFieldElement Sqrt() + { + return SquarePow(M - 1); + } + + public virtual int Representation + { + get { return F2mFieldElement.Ppb; } + } + + public virtual int M + { + get { return 571; } + } + + public virtual int K1 + { + get { return 2; } + } + + public virtual int K2 + { + get { return 5; } + } + + public virtual int K3 + { + get { return 10; } + } + + public override bool Equals(object obj) + { + return Equals(obj as SecT571FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecT571FieldElement); + } + + public virtual bool Equals(SecT571FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat576.Eq64(x, other.x); + } + + public override int GetHashCode() + { + return 5711052 ^ Arrays.GetHashCode(x, 0, 9); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs new file mode 100644
index 000000000..f5806f09c --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT571K1Curve + : AbstractF2mCurve + { + private const int SecT571K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT571K1Point m_infinity; + + public SecT571K1Curve() + : base(571, 2, 5, 10) + { + this.m_infinity = new SecT571K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.One); + this.m_order = new BigInteger(1, Hex.Decode("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001")); + this.m_cofactor = BigInteger.ValueOf(4); + + this.m_coord = SecT571K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT571K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected override ECMultiplier CreateDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 571; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT571FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT571K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT571K1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return true; } + } + + public virtual int M + { + get { return 571; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 2; } + } + + public virtual int K2 + { + get { return 5; } + } + + public virtual int K3 + { + get { return 10; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT571K1Point.cs b/crypto/src/math/ec/custom/sec/SecT571K1Point.cs new file mode 100644
index 000000000..62ed7bda0 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT571K1Point.cs
@@ -0,0 +1,296 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT571K1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT571K1Point(null, this.AffineXCoord, this.AffineYCoord); // earlier JDK + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + //return new SecT571K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT571K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT571K1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT571K1Point(curve, X3, curve.B, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT571K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.Square().Add(L1); + } + else + { + T = L1.Add(Z1).Multiply(L1); + } + + if (T.IsZero) + { + //return new SecT571K1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT571K1Point(curve, T, curve.B, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.Square(); + ECFieldElement L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3).Add(Z3); + + return new SecT571K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + // NOTE: TwicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = L1Sq.Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2plus1.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT571K1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT571K1Point(curve, A, curve.B, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT571K1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT571K1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs new file mode 100644
index 000000000..082afa5bd --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs
@@ -0,0 +1,102 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT571R1Curve + : AbstractF2mCurve + { + private const int SecT571R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected readonly SecT571R1Point m_infinity; + + internal static readonly SecT571FieldElement SecT571R1_B = new SecT571FieldElement( + new BigInteger(1, Hex.Decode("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A"))); + internal static readonly SecT571FieldElement SecT571R1_B_SQRT = (SecT571FieldElement)SecT571R1_B.Sqrt(); + + public SecT571R1Curve() + : base(571, 2, 5, 10) + { + this.m_infinity = new SecT571R1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.One); + this.m_b = SecT571R1_B; + this.m_order = new BigInteger(1, Hex.Decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47")); + this.m_cofactor = BigInteger.Two; + + this.m_coord = SecT571R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecT571R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return 571; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecT571FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecT571R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecT571R1Point(this, x, y, zs, withCompression); + } + + public override bool IsKoblitz + { + get { return false; } + } + + public virtual int M + { + get { return 571; } + } + + public virtual bool IsTrinomial + { + get { return false; } + } + + public virtual int K1 + { + get { return 2; } + } + + public virtual int K2 + { + get { return 5; } + } + + public virtual int K3 + { + get { return 10; } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecT571R1Point.cs b/crypto/src/math/ec/custom/sec/SecT571R1Point.cs new file mode 100644
index 000000000..0cbc98cf3 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecT571R1Point.cs
@@ -0,0 +1,286 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecT571R1Point + : AbstractF2mPoint + { + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecT571R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + + return Y; + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return false; + + ECFieldElement Y = this.RawYCoord; + + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + if (X1.IsZero) + { + if (X2.IsZero) + return curve.Infinity; + + return b.Add(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + return Twice(); + + return curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.XCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + //X3 = L.Square().Add(L).Add(X1).Add(curve.A); + X3 = L.Square().Add(L).Add(X1).AddOne(); + if (X3.IsZero) + { + //return new SecT571R1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT571R1Point(curve, X3, SecT571R1Curve.SecT571R1_B_SQRT, IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + //return new SecT571R1Point(curve, X3, curve.B.sqrt(), IsCompressed); + return new SecT571R1Point(curve, X3, SecT571R1Curve.SecT571R1_B_SQRT, IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new SecT571R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return curve.Infinity; + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement T = L1.Square().Add(L1Z1).Add(Z1Sq); + if (T.IsZero) + { + //return new SecT571R1Point(curve, T, curve.B.sqrt(), withCompression); + return new SecT571R1Point(curve, T, SecT571R1Curve.SecT571R1_B_SQRT, IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + + return new SecT571R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own Additive inverse + return b; + } + + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + //ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement T = Z1Sq.Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + //ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + return b.Twice(); + + return curve.Infinity; + } + + if (A.IsZero) + { + //return new SecT571R1Point(curve, A, curve.B.sqrt(), withCompression); + return new SecT571R1Point(curve, A, SecT571R1Curve.SecT571R1_B_SQRT, IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new SecT571R1Point(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new SecT571R1Point(Curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs b/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs
index fe683726f..517881323 100644 --- a/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs +++ b/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs
@@ -10,7 +10,13 @@ return p.Curve.Infinity; ECPoint positive = MultiplyPositive(p, k.Abs()); - return sign > 0 ? positive : positive.Negate(); + ECPoint result = sign > 0 ? positive : positive.Negate(); + + /* + * Although the various multipliers ought not to produce invalid output under normal + * circumstances, a final check here is advised to guard against fault attacks. + */ + return ECAlgorithms.ValidatePoint(result); } protected abstract ECPoint MultiplyPositive(ECPoint p, BigInteger k); diff --git a/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs b/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
index 832fd7be4..4848ada39 100644 --- a/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs +++ b/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
@@ -3,35 +3,9 @@ namespace Org.BouncyCastle.Math.EC.Multiplier public class ReferenceMultiplier : AbstractECMultiplier { - /** - * 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 <code>k * p</code>. - */ protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) { - ECPoint q = p.Curve.Infinity; - int t = k.BitLength; - if (t > 0) - { - if (k.TestBit(0)) - { - q = p; - } - for (int i = 1; i < t; i++) - { - p = p.Twice(); - if (k.TestBit(i)) - { - q = q.Add(p); - } - } - } - return q; + return ECAlgorithms.ReferenceMultiply(p, k); } } } diff --git a/crypto/src/math/ec/multiplier/WNafUtilities.cs b/crypto/src/math/ec/multiplier/WNafUtilities.cs
index 865b9073e..5491297d7 100644 --- a/crypto/src/math/ec/multiplier/WNafUtilities.cs +++ b/crypto/src/math/ec/multiplier/WNafUtilities.cs
@@ -10,6 +10,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier private static readonly byte[] EMPTY_BYTES = new byte[0]; private static readonly int[] EMPTY_INTS = new int[0]; + private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0]; public static int[] GenerateCompactNaf(BigInteger k) { @@ -368,46 +369,100 @@ namespace Org.BouncyCastle.Math.EC.Multiplier { ECCurve c = p.Curve; WNafPreCompInfo wnafPreCompInfo = GetWNafPreCompInfo(c.GetPreCompInfo(p, PRECOMP_NAME)); - + + int iniPreCompLen = 0, reqPreCompLen = 1 << System.Math.Max(0, width - 2); + ECPoint[] preComp = wnafPreCompInfo.PreComp; if (preComp == null) { - preComp = new ECPoint[]{ p }; + preComp = EMPTY_POINTS; + } + else + { + iniPreCompLen = preComp.Length; } - int preCompLen = preComp.Length; - int reqPreCompLen = 1 << System.Math.Max(0, width - 2); - - if (preCompLen < reqPreCompLen) + if (iniPreCompLen < reqPreCompLen) { preComp = ResizeTable(preComp, reqPreCompLen); - if (reqPreCompLen == 2) + + if (reqPreCompLen == 1) { - preComp[1] = preComp[0].ThreeTimes(); + preComp[0] = p.Normalize(); } else { - ECPoint twiceP = wnafPreCompInfo.Twice; - if (twiceP == null) + int curPreCompLen = iniPreCompLen; + if (curPreCompLen == 0) { - twiceP = preComp[0].Twice(); - wnafPreCompInfo.Twice = twiceP; + preComp[0] = p; + curPreCompLen = 1; } - for (int i = preCompLen; i < reqPreCompLen; i++) + ECFieldElement iso = null; + + if (reqPreCompLen == 2) { - /* - * 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]); + preComp[1] = p.ThreeTimes(); + } + else + { + ECPoint twiceP = wnafPreCompInfo.Twice, last = preComp[curPreCompLen - 1]; + if (twiceP == null) + { + twiceP = preComp[0].Twice(); + wnafPreCompInfo.Twice = twiceP; + + /* + * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism + * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This + * also requires scaling the initial point's X, Y coordinates, and reversing the + * isomorphism as part of the subsequent normalization. + * + * NOTE: The correctness of this optimization depends on: + * 1) additions do not use the curve's A, B coefficients. + * 2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ... + */ + if (ECAlgorithms.IsFpCurve(c) && c.FieldSize >= 64) + { + switch (c.CoordinateSystem) + { + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + iso = twiceP.GetZCoord(0); + twiceP = c.CreatePoint(twiceP.XCoord.ToBigInteger(), + twiceP.YCoord.ToBigInteger()); + + ECFieldElement iso2 = iso.Square(), iso3 = iso2.Multiply(iso); + last = last.ScaleX(iso2).ScaleY(iso3); + + if (iniPreCompLen == 0) + { + preComp[0] = last; + } + break; + } + } + } + } + + while (curPreCompLen < reqPreCompLen) + { + /* + * Compute the new ECPoints for the precomputation array. The values 1, 3, + * 5, ..., 2^(width-1)-1 times p are computed + */ + preComp[curPreCompLen++] = last = last.Add(twiceP); + } } - } - /* - * Having oft-used operands in affine form makes operations faster. - */ - c.NormalizeAll(preComp); + /* + * Having oft-used operands in affine form makes operations faster. + */ + c.NormalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso); + } } wnafPreCompInfo.PreComp = preComp; diff --git a/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs b/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs
index dda778eea..1e7ddae91 100644 --- a/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs +++ b/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs
@@ -15,23 +15,23 @@ namespace Org.BouncyCastle.Math.EC.Multiplier internal static readonly string PRECOMP_NAME = "bc_wtnaf"; /** - * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint} * by <code>k</code> using the reduced <code>&#964;</code>-adic NAF (RTNAF) * method. - * @param p The F2mPoint to multiply. + * @param p The AbstractF2mPoint to multiply. * @param k The integer by which to multiply <code>k</code>. * @return <code>p</code> multiplied by <code>k</code>. */ protected override ECPoint MultiplyPositive(ECPoint point, BigInteger k) { - 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(); + if (!(point is AbstractF2mPoint)) + throw new ArgumentException("Only AbstractF2mPoint can be used in WTauNafMultiplier"); + + AbstractF2mPoint p = (AbstractF2mPoint)point; + AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve; + int m = curve.FieldSize; + sbyte a = (sbyte)curve.A.ToBigInteger().IntValue; + sbyte mu = Tnaf.GetMu(a); BigInteger[] s = curve.GetSi(); ZTauElement rho = Tnaf.PartModReduction(k, m, a, s, mu, (sbyte)10); @@ -40,16 +40,16 @@ namespace Org.BouncyCastle.Math.EC.Multiplier } /** - * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint} * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> using * the <code>&#964;</code>-adic NAF (TNAF) method. - * @param p The F2mPoint to multiply. + * @param p The AbstractF2mPoint to multiply. * @param lambda The element <code>&#955;</code> of * <code><b>Z</b>[&#964;]</code> of which to compute the * <code>[&#964;]</code>-adic NAF. * @return <code>p</code> multiplied by <code>&#955;</code>. */ - private F2mPoint MultiplyWTnaf(F2mPoint p, ZTauElement lambda, + private AbstractF2mPoint MultiplyWTnaf(AbstractF2mPoint p, ZTauElement lambda, PreCompInfo preCompInfo, sbyte a, sbyte mu) { ZTauElement[] alpha = (a == 0) ? Tnaf.Alpha0 : Tnaf.Alpha1; @@ -63,20 +63,20 @@ namespace Org.BouncyCastle.Math.EC.Multiplier } /** - * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint} * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> * using the window <code>&#964;</code>-adic NAF (TNAF) method, given the * WTNAF of <code>&#955;</code>. - * @param p The F2mPoint to multiply. + * @param p The AbstractF2mPoint to multiply. * @param u The the WTNAF of <code>&#955;</code>.. * @return <code>&#955; * p</code> */ - private static F2mPoint MultiplyFromWTnaf(F2mPoint p, sbyte[] u, PreCompInfo preCompInfo) + private static AbstractF2mPoint MultiplyFromWTnaf(AbstractF2mPoint p, sbyte[] u, PreCompInfo preCompInfo) { - F2mCurve curve = (F2mCurve)p.Curve; + AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve; sbyte a = (sbyte)curve.A.ToBigInteger().IntValue; - F2mPoint[] pu; + AbstractF2mPoint[] pu; if ((preCompInfo == null) || !(preCompInfo is WTauNafPreCompInfo)) { pu = Tnaf.GetPreComp(p, a); @@ -90,26 +90,35 @@ namespace Org.BouncyCastle.Math.EC.Multiplier pu = ((WTauNafPreCompInfo)preCompInfo).PreComp; } + // TODO Include negations in precomp (optionally) and use from here + AbstractF2mPoint[] puNeg = new AbstractF2mPoint[pu.Length]; + for (int i = 0; i < pu.Length; ++i) + { + puNeg[i] = (AbstractF2mPoint)pu[i].Negate(); + } + + // q = infinity - F2mPoint q = (F2mPoint)curve.Infinity; + AbstractF2mPoint q = (AbstractF2mPoint) p.Curve.Infinity; + + int tauCount = 0; for (int i = u.Length - 1; i >= 0; i--) { - q = Tnaf.Tau(q); - sbyte ui = u[i]; + ++tauCount; + int ui = u[i]; if (ui != 0) { - if (ui > 0) - { - q = q.AddSimple(pu[ui]); - } - else - { - // u[i] < 0 - q = q.SubtractSimple(pu[-ui]); - } + q = q.TauPow(tauCount); + tauCount = 0; + + ECPoint x = ui > 0 ? pu[ui >> 1] : puNeg[(-ui) >> 1]; + q = (AbstractF2mPoint)q.Add(x); } } - + if (tauCount > 0) + { + q = q.TauPow(tauCount); + } return q; } } diff --git a/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs b/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs
index 3c18404c0..72659b3ec 100644 --- a/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs +++ b/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs
@@ -8,14 +8,14 @@ namespace Org.BouncyCastle.Math.EC.Multiplier : PreCompInfo { /** - * Array holding the precomputed <code>F2mPoint</code>s used for the + * Array holding the precomputed <code>AbstractF2mPoint</code>s used for the * WTNAF multiplication in <code> * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() * WTauNafMultiplier.multiply()}</code>. */ - protected F2mPoint[] m_preComp; + protected AbstractF2mPoint[] m_preComp; - public virtual F2mPoint[] PreComp + public virtual AbstractF2mPoint[] PreComp { get { return m_preComp; } set { this.m_preComp = value; } diff --git a/crypto/src/math/raw/Interleave.cs b/crypto/src/math/raw/Interleave.cs new file mode 100644
index 000000000..a45ee1e08 --- /dev/null +++ b/crypto/src/math/raw/Interleave.cs
@@ -0,0 +1,95 @@ +using System; + +namespace Org.BouncyCastle.Math.Raw +{ + internal abstract class Interleave + { + private const ulong M32 = 0x55555555UL; + private const ulong M64 = 0x5555555555555555UL; + + /* + * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits. + * In a binary field, this operation is the same as squaring an 8 bit number. + */ + //private static readonly ushort[] INTERLEAVE2_TABLE = new ushort[] + //{ + // 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, + // 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, + // 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, + // 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, + // 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, + // 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, + // 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, + // 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, + // 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, + // 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, + // 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, + // 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, + // 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, + // 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, + // 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, + // 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, + // 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, + // 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, + // 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, + // 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, + // 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, + // 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, + // 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, + // 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, + // 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, + // 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, + // 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, + // 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, + // 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, + // 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, + // 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, + // 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555 + //}; + + internal static uint Expand8to16(uint x) + { + x &= 0xFFU; + x = (x | (x << 4)) & 0x0F0FU; + x = (x | (x << 2)) & 0x3333U; + x = (x | (x << 1)) & 0x5555U; + return x; + } + + internal static uint Expand16to32(uint x) + { + x &= 0xFFFFU; + x = (x | (x << 8)) & 0x00FF00FFU; + x = (x | (x << 4)) & 0x0F0F0F0FU; + x = (x | (x << 2)) & 0x33333333U; + x = (x | (x << 1)) & 0x55555555U; + return x; + } + + internal static ulong Expand32to64(uint x) + { + // "shuffle" low half to even bits and high half to odd bits + uint t; + t = (x ^ (x >> 8)) & 0x0000FF00U; x ^= (t ^ (t << 8)); + t = (x ^ (x >> 4)) & 0x00F000F0U; x ^= (t ^ (t << 4)); + t = (x ^ (x >> 2)) & 0x0C0C0C0CU; x ^= (t ^ (t << 2)); + t = (x ^ (x >> 1)) & 0x22222222U; x ^= (t ^ (t << 1)); + + return ((x >> 1) & M32) << 32 | (x & M32); + } + + internal static void Expand64To128(ulong x, ulong[] z, int zOff) + { + // "shuffle" low half to even bits and high half to odd bits + ulong t; + t = (x ^ (x >> 16)) & 0x00000000FFFF0000UL; x ^= (t ^ (t << 16)); + t = (x ^ (x >> 8)) & 0x0000FF000000FF00UL; x ^= (t ^ (t << 8)); + t = (x ^ (x >> 4)) & 0x00F000F000F000F0UL; x ^= (t ^ (t << 4)); + t = (x ^ (x >> 2)) & 0x0C0C0C0C0C0C0C0CUL; x ^= (t ^ (t << 2)); + t = (x ^ (x >> 1)) & 0x2222222222222222UL; x ^= (t ^ (t << 1)); + + z[zOff ] = (x ) & M64; + z[zOff + 1] = (x >> 1) & M64; + } + } +} diff --git a/crypto/src/math/ec/Mod.cs b/crypto/src/math/raw/Mod.cs
index 37958e57e..63467e668 100644 --- a/crypto/src/math/ec/Mod.cs +++ b/crypto/src/math/raw/Mod.cs
@@ -4,7 +4,7 @@ using System.Diagnostics; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; -namespace Org.BouncyCastle.Math.EC +namespace Org.BouncyCastle.Math.Raw { internal abstract class Mod { @@ -49,9 +49,9 @@ namespace Org.BouncyCastle.Math.EC if (Nat.Gte(len, u, v)) { - Nat.Sub(len, u, v, u); + Nat.SubFrom(len, v, u); Debug.Assert((u[0] & 1) == 0); - ac += Nat.Sub(len, a, b, a) - bc; + ac += Nat.SubFrom(len, b, a) - bc; InversionStep(p, u, uvLen, a, ref ac); if (Nat.IsOne(len, u)) { @@ -61,9 +61,9 @@ namespace Org.BouncyCastle.Math.EC } else { - Nat.Sub(len, v, u, v); + Nat.SubFrom(len, u, v); Debug.Assert((v[0] & 1) == 0); - bc += Nat.Sub(len, b, a, b) - ac; + bc += Nat.SubFrom(len, a, b) - ac; InversionStep(p, v, uvLen, b, ref bc); if (Nat.IsOne(len, v)) { @@ -99,13 +99,23 @@ namespace Org.BouncyCastle.Math.EC return s; } + public static void Add(uint[] p, uint[] x, uint[] y, uint[] z) + { + int len = p.Length; + uint c = Nat.Add(len, x, y, z); + if (c != 0) + { + Nat.SubFrom(len, p, z); + } + } + public static void Subtract(uint[] p, uint[] x, uint[] y, uint[] z) { int len = p.Length; int c = Nat.Sub(len, x, y, z); if (c != 0) { - Nat.Add(len, z, p, z); + Nat.AddTo(len, p, z); } } @@ -146,11 +156,11 @@ namespace Org.BouncyCastle.Math.EC { if (xc < 0) { - xc += (int)Nat.Add(len, x, p, x); + xc += (int)Nat.AddTo(len, p, x); } else { - xc += Nat.Sub(len, x, p, x); + xc += Nat.SubFrom(len, p, x); } } diff --git a/crypto/src/math/ec/Nat.cs b/crypto/src/math/raw/Nat.cs
index 17b632f26..1f9ab00ec 100644 --- a/crypto/src/math/ec/Nat.cs +++ b/crypto/src/math/raw/Nat.cs
@@ -3,7 +3,7 @@ using System.Diagnostics; using Org.BouncyCastle.Crypto.Utilities; -namespace Org.BouncyCastle.Math.EC +namespace Org.BouncyCastle.Math.Raw { internal abstract class Nat { @@ -212,6 +212,11 @@ namespace Org.BouncyCastle.Math.EC return new uint[len]; } + public static ulong[] Create64(int len) + { + return new ulong[len]; + } + public static int Dec(int len, uint[] z) { for (int i = 0; i < len; ++i) @@ -666,6 +671,17 @@ namespace Org.BouncyCastle.Math.EC return c >> 31; } + public static ulong ShiftUpBit64(int len, ulong[] x, int xOff, ulong c, ulong[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + ulong next = x[xOff + i]; + z[zOff + i] = (next << 1) | (c >> 63); + c = next; + } + return c >> 63; + } + public static uint ShiftUpBits(int len, uint[] z, int bits, uint c) { Debug.Assert(bits > 0 && bits < 32); @@ -690,6 +706,18 @@ namespace Org.BouncyCastle.Math.EC return c >> -bits; } + public static ulong ShiftUpBits64(int len, ulong[] z, int zOff, int bits, ulong c) + { + Debug.Assert(bits > 0 && bits < 64); + for (int i = 0; i < len; ++i) + { + ulong next = z[zOff + i]; + z[zOff + i] = (next << bits) | (c >> -bits); + c = next; + } + return c >> -bits; + } + public static uint ShiftUpBits(int len, uint[] x, int bits, uint c, uint[] z) { Debug.Assert(bits > 0 && bits < 32); @@ -714,6 +742,18 @@ namespace Org.BouncyCastle.Math.EC return c >> -bits; } + public static ulong ShiftUpBits64(int len, ulong[] x, int xOff, int bits, ulong c, ulong[] z, int zOff) + { + Debug.Assert(bits > 0 && bits < 64); + for (int i = 0; i < len; ++i) + { + ulong next = x[xOff + i]; + z[zOff + i] = (next << bits) | (c >> -bits); + c = next; + } + return c >> -bits; + } + public static void Square(int len, uint[] x, uint[] zz) { int extLen = len << 1; diff --git a/crypto/src/math/raw/Nat128.cs b/crypto/src/math/raw/Nat128.cs new file mode 100644
index 000000000..819c52062 --- /dev/null +++ b/crypto/src/math/raw/Nat128.cs
@@ -0,0 +1,856 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.Raw +{ + internal abstract class Nat128 + { + private const ulong M = 0xFFFFFFFFUL; + + public static uint Add(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn) + { + ulong c = cIn; + c += (ulong)x[xOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff) + { + ulong c = 0; + c += (ulong)u[uOff + 0] + v[vOff + 0]; + u[uOff + 0] = (uint)c; + v[vOff + 0] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 1] + v[vOff + 1]; + u[uOff + 1] = (uint)c; + v[vOff + 1] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 2] + v[vOff + 2]; + u[uOff + 2] = (uint)c; + v[vOff + 2] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 3] + v[vOff + 3]; + u[uOff + 3] = (uint)c; + v[vOff + 3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static void Copy(uint[] x, uint[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static void Copy64(ulong[] x, ulong[] z) + { + z[0] = x[0]; + z[1] = x[1]; + } + + public static uint[] Create() + { + return new uint[4]; + } + + public static ulong[] Create64() + { + return new ulong[2]; + } + + public static uint[] CreateExt() + { + return new uint[8]; + } + + public static ulong[] CreateExt64() + { + return new ulong[4]; + } + + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + bool pos = Gte(x, xOff, y, yOff); + if (pos) + { + Sub(x, xOff, y, yOff, z, zOff); + } + else + { + Sub(y, yOff, x, xOff, z, zOff); + } + return pos; + } + + public static bool Eq(uint[] x, uint[] y) + { + for (int i = 3; i >= 0; --i) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + public static bool Eq64(ulong[] x, ulong[] y) + { + for (int i = 1; i >= 0; --i) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + public static uint[] FromBigInteger(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 128) + throw new ArgumentException(); + + uint[] z = Create(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (uint)x.IntValue; + x = x.ShiftRight(32); + } + return z; + } + + public static ulong[] FromBigInteger64(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 128) + throw new ArgumentException(); + + ulong[] z = Create64(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (ulong)x.LongValue; + x = x.ShiftRight(64); + } + return z; + } + + public static uint GetBit(uint[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + if ((bit & 127) != bit) + { + return 0; + } + int w = bit >> 5; + int b = bit & 31; + return (x[w] >> b) & 1; + } + + public static bool Gte(uint[] x, uint[] y) + { + for (int i = 3; i >= 0; --i) + { + uint x_i = x[i], y_i = y[i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool Gte(uint[] x, int xOff, uint[] y, int yOff) + { + for (int i = 3; i >= 0; --i) + { + uint x_i = x[xOff + i], y_i = y[yOff + i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool IsOne(uint[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 4; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsOne64(ulong[] x) + { + if (x[0] != 1UL) + { + return false; + } + for (int i = 1; i < 2; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static bool IsZero(uint[] x) + { + for (int i = 0; i < 4; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsZero64(ulong[] x) + { + for (int i = 0; i < 2; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + + { + ulong c = 0, x_0 = x[0]; + c += x_0 * y_0; + zz[0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[3] = (uint)c; + c >>= 32; + zz[4] = (uint)c; + } + + for (int i = 1; i < 4; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + zz[i + 4] = (uint)c; + } + } + + public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + + { + ulong c = 0, x_0 = x[xOff + 0]; + c += x_0 * y_0; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (uint)c; + c >>= 32; + zz[zzOff + 4] = (uint)c; + } + + for (int i = 1; i < 4; ++i) + { + ++zzOff; + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + zz[zzOff + 4] = (uint)c; + } + } + + public static uint MulAddTo(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + + ulong zc = 0; + for (int i = 0; i < 4; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += zc + zz[i + 4]; + zz[i + 4] = (uint)c; + zc = c >> 32; + } + return (uint)zc; + } + + public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + + ulong zc = 0; + for (int i = 0; i < 4; ++i) + { + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += zc + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + zc = c >> 32; + ++zzOff; + } + return (uint)zc; + } + + public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + Debug.Assert(w >> 31 == 0); + + ulong c = 0, wVal = w; + ulong x0 = x[xOff + 0]; + c += wVal * x0 + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong x1 = x[xOff + 1]; + c += wVal * x1 + x0 + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + ulong x2 = x[xOff + 2]; + c += wVal * x2 + x1 + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + ulong x3 = x[xOff + 3]; + c += wVal * x3 + x2 + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += x3; + return c; + } + + public static uint MulWordAddExt(uint x, uint[] yy, int yyOff, uint[] zz, int zzOff) + { + Debug.Assert(yyOff <= 4); + Debug.Assert(zzOff <= 4); + + ulong c = 0, xVal = x; + c += xVal * yy[yyOff + 0] + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 1] + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 2] + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 3] + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 0); + ulong c = 0, xVal = x; + ulong y00 = y & M; + c += xVal * y00 + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong y01 = y >> 32; + c += xVal * y01 + y00 + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += y01 + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 1); + ulong c = 0, yVal = y; + c += yVal * x + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += yVal + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(4, z, zOff, 3); + } + + public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 1); + ulong c = 0, xVal = x; + c += xVal * y + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * (y >> 32) + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(4, z, zOff, 3); + } + + public static uint MulWordsAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 2); + + ulong c = 0, xVal = x, yVal = y; + c += yVal * xVal + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(4, z, zOff, 2); + } + + public static uint MulWord(uint x, uint[] y, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + int i = 0; + do + { + c += xVal * y[i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < 4); + return (uint)c; + } + + public static void Square(uint[] x, uint[] zz) + { + ulong x_0 = x[0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 3, j = 8; + do + { + ulong xVal = x[i--]; + ulong p = xVal * xVal; + zz[--j] = (c << 31) | (uint)(p >> 33); + zz[--j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[1]; + ulong zz_2 = zz[2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[2]; + ulong zz_3 = zz[3]; + ulong zz_4 = zz[4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[3]; + ulong zz_5 = zz[5]; + ulong zz_6 = zz[6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_6 += zz_5 >> 32; + } + + w = (uint)zz_4; + zz[4] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_5; + zz[5] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_6; + zz[6] = (w << 1) | c; + c = w >> 31; + w = zz[7] + (uint)(zz_6 >> 32); + zz[7] = (w << 1) | c; + } + + public static void Square(uint[] x, int xOff, uint[] zz, int zzOff) + { + ulong x_0 = x[xOff + 0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 3, j = 8; + do + { + ulong xVal = x[xOff + i--]; + ulong p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (uint)(p >> 33); + zz[zzOff + --j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[zzOff + 0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[xOff + 1]; + ulong zz_2 = zz[zzOff + 2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[xOff + 2]; + ulong zz_3 = zz[zzOff + 3]; + ulong zz_4 = zz[zzOff + 4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[zzOff + 2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[xOff + 3]; + ulong zz_5 = zz[zzOff + 5]; + ulong zz_6 = zz[zzOff + 6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[zzOff + 3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_6 += zz_5 >> 32; + } + + w = (uint)zz_4; + zz[zzOff + 4] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >> 31; + w = zz[zzOff + 7] + (uint)(zz_6 >> 32); + zz[zzOff + 7] = (w << 1) | c; + } + + public static int Sub(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + c += (long)x[xOff + 0] - y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)x[xOff + 1] - y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)x[xOff + 2] - y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)x[xOff + 3] - y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubBothFrom(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3]; + z[3] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff) + { + long c = 0; + c += (long)z[zOff + 0] - x[xOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - x[xOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)z[zOff + 2] - x[xOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)z[zOff + 3] - x[xOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return (int)c; + } + + public static BigInteger ToBigInteger(uint[] x) + { + byte[] bs = new byte[16]; + for (int i = 0; i < 4; ++i) + { + uint x_i = x[i]; + if (x_i != 0) + { + Pack.UInt32_To_BE(x_i, bs, (3 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static BigInteger ToBigInteger64(ulong[] x) + { + byte[] bs = new byte[16]; + for (int i = 0; i < 2; ++i) + { + ulong x_i = x[i]; + if (x_i != 0UL) + { + Pack.UInt64_To_BE(x_i, bs, (1 - i) << 3); + } + } + return new BigInteger(1, bs); + } + + public static void Zero(uint[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + } + } +} diff --git a/crypto/src/math/raw/Nat160.cs b/crypto/src/math/raw/Nat160.cs new file mode 100644
index 000000000..153ac0a43 --- /dev/null +++ b/crypto/src/math/raw/Nat160.cs
@@ -0,0 +1,874 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.Raw +{ + internal abstract class Nat160 + { + private const ulong M = 0xFFFFFFFFUL; + + public static uint Add(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4]; + z[4] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn) + { + ulong c = cIn; + c += (ulong)x[xOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + z[zOff + 5]; + return (uint)c; + } + + public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff) + { + ulong c = 0; + c += (ulong)u[uOff + 0] + v[vOff + 0]; + u[uOff + 0] = (uint)c; + v[vOff + 0] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 1] + v[vOff + 1]; + u[uOff + 1] = (uint)c; + v[vOff + 1] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 2] + v[vOff + 2]; + u[uOff + 2] = (uint)c; + v[vOff + 2] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 3] + v[vOff + 3]; + u[uOff + 3] = (uint)c; + v[vOff + 3] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 4] + v[vOff + 4]; + u[uOff + 4] = (uint)c; + v[vOff + 4] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static void Copy(uint[] x, uint[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + } + + public static uint[] Create() + { + return new uint[5]; + } + + public static uint[] CreateExt() + { + return new uint[10]; + } + + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + bool pos = Gte(x, xOff, y, yOff); + if (pos) + { + Sub(x, xOff, y, yOff, z, zOff); + } + else + { + Sub(y, yOff, x, xOff, z, zOff); + } + return pos; + } + + public static bool Eq(uint[] x, uint[] y) + { + for (int i = 4; i >= 0; --i) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + public static uint[] FromBigInteger(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 160) + throw new ArgumentException(); + + uint[] z = Create(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (uint)x.IntValue; + x = x.ShiftRight(32); + } + return z; + } + + public static uint GetBit(uint[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + int w = bit >> 5; + if (w < 0 || w >= 5) + { + return 0; + } + int b = bit & 31; + return (x[w] >> b) & 1; + } + + public static bool Gte(uint[] x, uint[] y) + { + for (int i = 4; i >= 0; --i) + { + uint x_i = x[i], y_i = y[i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool Gte(uint[] x, int xOff, uint[] y, int yOff) + { + for (int i = 4; i >= 0; --i) + { + uint x_i = x[xOff + i], y_i = y[yOff + i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool IsOne(uint[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 5; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsZero(uint[] x) + { + for (int i = 0; i < 5; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + + { + ulong c = 0, x_0 = x[0]; + c += x_0 * y_0; + zz[0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[4] = (uint)c; + c >>= 32; + zz[5] = (uint)c; + } + + for (int i = 1; i < 5; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + zz[i + 5] = (uint)c; + } + } + + public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + + { + ulong c = 0, x_0 = x[xOff + 0]; + c += x_0 * y_0; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[zzOff + 4] = (uint)c; + c >>= 32; + zz[zzOff + 5] = (uint)c; + } + + for (int i = 1; i < 5; ++i) + { + ++zzOff; + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + zz[zzOff + 5] = (uint)c; + } + } + + public static uint MulAddTo(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + + ulong zc = 0; + for (int i = 0; i < 5; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += zc + zz[i + 5]; + zz[i + 5] = (uint)c; + zc = c >> 32; + } + return (uint)zc; + } + + public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + + ulong zc = 0; + for (int i = 0; i < 5; ++i) + { + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += zc + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + zc = c >> 32; + ++zzOff; + } + return (uint)zc; + } + + public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + Debug.Assert(w >> 31 == 0); + + ulong c = 0, wVal = w; + ulong x0 = x[xOff + 0]; + c += wVal * x0 + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong x1 = x[xOff + 1]; + c += wVal * x1 + x0 + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + ulong x2 = x[xOff + 2]; + c += wVal * x2 + x1 + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + ulong x3 = x[xOff + 3]; + c += wVal * x3 + x2 + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + ulong x4 = x[xOff + 4]; + c += wVal * x4 + x3 + y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += x4; + return c; + } + + public static uint MulWordAddExt(uint x, uint[] yy, int yyOff, uint[] zz, int zzOff) + { + Debug.Assert(yyOff <= 5); + Debug.Assert(zzOff <= 5); + + ulong c = 0, xVal = x; + c += xVal * yy[yyOff + 0] + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 1] + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 2] + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 3] + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 4] + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 1); + ulong c = 0, xVal = x; + ulong y00 = y & M; + c += xVal * y00 + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong y01 = y >> 32; + c += xVal * y01 + y00 + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += y01 + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(5, z, zOff, 4); + } + + public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 2); + ulong c = 0, yVal = y; + c += yVal * x + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += yVal + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(5, z, zOff, 3); + } + + public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 2); + ulong c = 0, xVal = x; + c += xVal * y + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * (y >> 32) + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(5, z, zOff, 3); + } + + public static uint MulWordsAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 3); + + ulong c = 0, xVal = x, yVal = y; + c += yVal * xVal + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(5, z, zOff, 2); + } + + public static uint MulWord(uint x, uint[] y, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + int i = 0; + do + { + c += xVal * y[i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < 5); + return (uint)c; + } + + public static void Square(uint[] x, uint[] zz) + { + ulong x_0 = x[0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 4, j = 10; + do + { + ulong xVal = x[i--]; + ulong p = xVal * xVal; + zz[--j] = (c << 31) | (uint)(p >> 33); + zz[--j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[1]; + ulong zz_2 = zz[2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[2]; + ulong zz_3 = zz[3]; + ulong zz_4 = zz[4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[3]; + ulong zz_5 = zz[5]; + ulong zz_6 = zz[6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[4]; + ulong zz_7 = zz[7]; + ulong zz_8 = zz[8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_8 += zz_7 >> 32; + } + + w = (uint)zz_5; + zz[5] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_6; + zz[6] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_7; + zz[7] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_8; + zz[8] = (w << 1) | c; + c = w >> 31; + w = zz[9] + (uint)(zz_8 >> 32); + zz[9] = (w << 1) | c; + } + + public static void Square(uint[] x, int xOff, uint[] zz, int zzOff) + { + ulong x_0 = x[xOff + 0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 4, j = 10; + do + { + ulong xVal = x[xOff + i--]; + ulong p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (uint)(p >> 33); + zz[zzOff + --j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[zzOff + 0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[xOff + 1]; + ulong zz_2 = zz[zzOff + 2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[xOff + 2]; + ulong zz_3 = zz[zzOff + 3]; + ulong zz_4 = zz[zzOff + 4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[zzOff + 2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[xOff + 3]; + ulong zz_5 = zz[zzOff + 5]; + ulong zz_6 = zz[zzOff + 6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[zzOff + 3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[xOff + 4]; + ulong zz_7 = zz[zzOff + 7]; + ulong zz_8 = zz[zzOff + 8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[zzOff + 4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_8 += zz_7 >> 32; + } + + w = (uint)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_7; + zz[zzOff + 7] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_8; + zz[zzOff + 8] = (w << 1) | c; + c = w >> 31; + w = zz[zzOff + 9] + (uint)(zz_8 >> 32); + zz[zzOff + 9] = (w << 1) | c; + } + + public static int Sub(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + c += (long)x[xOff + 0] - y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)x[xOff + 1] - y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)x[xOff + 2] - y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)x[xOff + 3] - y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)x[xOff + 4] - y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubBothFrom(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4]; + z[4] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff) + { + long c = 0; + c += (long)z[zOff + 0] - x[xOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - x[xOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)z[zOff + 2] - x[xOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)z[zOff + 3] - x[xOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)z[zOff + 4] - x[xOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + return (int)c; + } + + public static BigInteger ToBigInteger(uint[] x) + { + byte[] bs = new byte[20]; + for (int i = 0; i < 5; ++i) + { + uint x_i = x[i]; + if (x_i != 0) + { + Pack.UInt32_To_BE(x_i, bs, (4 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static void Zero(uint[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + z[4] = 0; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat192.cs b/crypto/src/math/raw/Nat192.cs
index 94d7ed17c..4797609ee 100644 --- a/crypto/src/math/ec/custom/sec/Nat192.cs +++ b/crypto/src/math/raw/Nat192.cs
@@ -3,7 +3,7 @@ using System.Diagnostics; using Org.BouncyCastle.Crypto.Utilities; -namespace Org.BouncyCastle.Math.EC.Custom.Sec +namespace Org.BouncyCastle.Math.Raw { internal abstract class Nat192 { @@ -145,16 +145,33 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec z[5] = x[5]; } + public static void Copy64(ulong[] x, ulong[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + } + public static uint[] Create() { return new uint[6]; } + public static ulong[] Create64() + { + return new ulong[3]; + } + public static uint[] CreateExt() { return new uint[12]; } + public static ulong[] CreateExt64() + { + return new ulong[6]; + } + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) { bool pos = Gte(x, xOff, y, yOff); @@ -179,6 +196,18 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return true; } + public static bool Eq64(ulong[] x, ulong[] y) + { + for (int i = 2; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + public static uint[] FromBigInteger(BigInteger x) { if (x.SignValue < 0 || x.BitLength > 192) @@ -194,6 +223,21 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return z; } + public static ulong[] FromBigInteger64(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 192) + throw new ArgumentException(); + + ulong[] z = Create64(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (ulong)x.LongValue; + x = x.ShiftRight(64); + } + return z; + } + public static uint GetBit(uint[] x, int bit) { if (bit == 0) @@ -251,6 +295,22 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return true; } + public static bool IsOne64(ulong[] x) + { + if (x[0] != 1UL) + { + return false; + } + for (int i = 1; i < 3; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + public static bool IsZero(uint[] x) { for (int i = 0; i < 6; ++i) @@ -263,6 +323,18 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return true; } + public static bool IsZero64(ulong[] x) + { + for (int i = 0; i < 3; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + public static void Mul(uint[] x, uint[] y, uint[] zz) { ulong y_0 = y[0]; @@ -949,6 +1021,20 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new BigInteger(1, bs); } + public static BigInteger ToBigInteger64(ulong[] x) + { + byte[] bs = new byte[24]; + for (int i = 0; i < 3; ++i) + { + ulong x_i = x[i]; + if (x_i != 0L) + { + Pack.UInt64_To_BE(x_i, bs, (2 - i) << 3); + } + } + return new BigInteger(1, bs); + } + public static void Zero(uint[] z) { z[0] = 0; diff --git a/crypto/src/math/ec/custom/sec/Nat224.cs b/crypto/src/math/raw/Nat224.cs
index d5b916a54..940e930ac 100644 --- a/crypto/src/math/ec/custom/sec/Nat224.cs +++ b/crypto/src/math/raw/Nat224.cs
@@ -3,7 +3,7 @@ using System.Diagnostics; using Org.BouncyCastle.Crypto.Utilities; -namespace Org.BouncyCastle.Math.EC.Custom.Sec +namespace Org.BouncyCastle.Math.Raw { internal abstract class Nat224 { diff --git a/crypto/src/math/ec/custom/sec/Nat256.cs b/crypto/src/math/raw/Nat256.cs
index bd2d6da47..19455031a 100644 --- a/crypto/src/math/ec/custom/sec/Nat256.cs +++ b/crypto/src/math/raw/Nat256.cs
@@ -3,7 +3,7 @@ using System.Diagnostics; using Org.BouncyCastle.Crypto.Utilities; -namespace Org.BouncyCastle.Math.EC.Custom.Sec +namespace Org.BouncyCastle.Math.Raw { internal abstract class Nat256 { @@ -239,16 +239,34 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec z[7] = x[7]; } + public static void Copy64(ulong[] x, ulong[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + public static uint[] Create() { return new uint[8]; } + public static ulong[] Create64() + { + return new ulong[4]; + } + public static uint[] CreateExt() { return new uint[16]; } + public static ulong[] CreateExt64() + { + return new ulong[8]; + } + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) { bool pos = Gte(x, xOff, y, yOff); @@ -273,6 +291,18 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return true; } + public static bool Eq64(ulong[] x, ulong[] y) + { + for (int i = 3; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + public static uint[] FromBigInteger(BigInteger x) { if (x.SignValue < 0 || x.BitLength > 256) @@ -288,6 +318,21 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return z; } + public static ulong[] FromBigInteger64(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 256) + throw new ArgumentException(); + + ulong[] z = Create64(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (ulong)x.LongValue; + x = x.ShiftRight(64); + } + return z; + } + public static uint GetBit(uint[] x, int bit) { if (bit == 0) @@ -345,6 +390,22 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return true; } + public static bool IsOne64(ulong[] x) + { + if (x[0] != 1UL) + { + return false; + } + for (int i = 1; i < 4; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + public static bool IsZero(uint[] x) { for (int i = 0; i < 8; ++i) @@ -357,6 +418,18 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return true; } + public static bool IsZero64(ulong[] x) + { + for (int i = 0; i < 4; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + public static void Mul(uint[] x, uint[] y, uint[] zz) { ulong y_0 = y[0]; @@ -1285,6 +1358,20 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec return new BigInteger(1, bs); } + public static BigInteger ToBigInteger64(ulong[] x) + { + byte[] bs = new byte[32]; + for (int i = 0; i < 4; ++i) + { + ulong x_i = x[i]; + if (x_i != 0L) + { + Pack.UInt64_To_BE(x_i, bs, (3 - i) << 3); + } + } + return new BigInteger(1, bs); + } + public static void Zero(uint[] z) { z[0] = 0; diff --git a/crypto/src/math/raw/Nat320.cs b/crypto/src/math/raw/Nat320.cs new file mode 100644
index 000000000..c7daa71e2 --- /dev/null +++ b/crypto/src/math/raw/Nat320.cs
@@ -0,0 +1,98 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.Raw +{ + internal abstract class Nat320 + { + public static void Copy64(ulong[] x, ulong[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + } + + public static ulong[] Create64() + { + return new ulong[5]; + } + + public static ulong[] CreateExt64() + { + return new ulong[10]; + } + + public static bool Eq64(ulong[] x, ulong[] y) + { + for (int i = 4; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static ulong[] FromBigInteger64(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 320) + throw new ArgumentException(); + + ulong[] z = Create64(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (ulong)x.LongValue; + x = x.ShiftRight(64); + } + return z; + } + + public static bool IsOne64(ulong[] x) + { + if (x[0] != 1UL) + { + return false; + } + for (int i = 1; i < 5; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static bool IsZero64(ulong[] x) + { + for (int i = 0; i < 5; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static BigInteger ToBigInteger64(ulong[] x) + { + byte[] bs = new byte[40]; + for (int i = 0; i < 5; ++i) + { + ulong x_i = x[i]; + if (x_i != 0L) + { + Pack.UInt64_To_BE(x_i, bs, (4 - i) << 3); + } + } + return new BigInteger(1, bs); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat384.cs b/crypto/src/math/raw/Nat384.cs
index dd93e68b6..ed1c47e8c 100644 --- a/crypto/src/math/ec/custom/sec/Nat384.cs +++ b/crypto/src/math/raw/Nat384.cs
@@ -1,7 +1,7 @@ using System; using System.Diagnostics; -namespace Org.BouncyCastle.Math.EC.Custom.Sec +namespace Org.BouncyCastle.Math.Raw { internal abstract class Nat384 { diff --git a/crypto/src/math/raw/Nat448.cs b/crypto/src/math/raw/Nat448.cs new file mode 100644
index 000000000..52a253f1b --- /dev/null +++ b/crypto/src/math/raw/Nat448.cs
@@ -0,0 +1,100 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.Raw +{ + internal abstract class Nat448 + { + public static void Copy64(ulong[] x, ulong[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + z[5] = x[5]; + z[6] = x[6]; + } + + public static ulong[] Create64() + { + return new ulong[7]; + } + + public static ulong[] CreateExt64() + { + return new ulong[14]; + } + + public static bool Eq64(ulong[] x, ulong[] y) + { + for (int i = 6; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static ulong[] FromBigInteger64(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 448) + throw new ArgumentException(); + + ulong[] z = Create64(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (ulong)x.LongValue; + x = x.ShiftRight(64); + } + return z; + } + + public static bool IsOne64(ulong[] x) + { + if (x[0] != 1UL) + { + return false; + } + for (int i = 1; i < 7; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static bool IsZero64(ulong[] x) + { + for (int i = 0; i < 7; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static BigInteger ToBigInteger64(ulong[] x) + { + byte[] bs = new byte[56]; + for (int i = 0; i < 7; ++i) + { + ulong x_i = x[i]; + if (x_i != 0L) + { + Pack.UInt64_To_BE(x_i, bs, (6 - i) << 3); + } + } + return new BigInteger(1, bs); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat512.cs b/crypto/src/math/raw/Nat512.cs
index 46e10f995..a9ef2b3b6 100644 --- a/crypto/src/math/ec/custom/sec/Nat512.cs +++ b/crypto/src/math/raw/Nat512.cs
@@ -1,7 +1,7 @@ using System; using System.Diagnostics; -namespace Org.BouncyCastle.Math.EC.Custom.Sec +namespace Org.BouncyCastle.Math.Raw { internal abstract class Nat512 { diff --git a/crypto/src/math/raw/Nat576.cs b/crypto/src/math/raw/Nat576.cs new file mode 100644
index 000000000..813fb86be --- /dev/null +++ b/crypto/src/math/raw/Nat576.cs
@@ -0,0 +1,102 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.Raw +{ + internal abstract class Nat576 + { + public static void Copy64(ulong[] x, ulong[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + z[5] = x[5]; + z[6] = x[6]; + z[7] = x[7]; + z[8] = x[8]; + } + + public static ulong[] Create64() + { + return new ulong[9]; + } + + public static ulong[] CreateExt64() + { + return new ulong[18]; + } + + public static bool Eq64(ulong[] x, ulong[] y) + { + for (int i = 8; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static ulong[] FromBigInteger64(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 576) + throw new ArgumentException(); + + ulong[] z = Create64(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (ulong)x.LongValue; + x = x.ShiftRight(64); + } + return z; + } + + public static bool IsOne64(ulong[] x) + { + if (x[0] != 1UL) + { + return false; + } + for (int i = 1; i < 9; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static bool IsZero64(ulong[] x) + { + for (int i = 0; i < 9; ++i) + { + if (x[i] != 0UL) + { + return false; + } + } + return true; + } + + public static BigInteger ToBigInteger64(ulong[] x) + { + byte[] bs = new byte[72]; + for (int i = 0; i < 9; ++i) + { + ulong x_i = x[i]; + if (x_i != 0L) + { + Pack.UInt64_To_BE(x_i, bs, (8 - i) << 3); + } + } + return new BigInteger(1, bs); + } + } +} diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
index f46f99d37..2a2e63961 100644 --- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs +++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
@@ -3,10 +3,13 @@ using System.Collections; using System.Diagnostics; using System.IO; +using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; @@ -79,70 +82,133 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp : EncMethod { internal PgpPublicKey pubKey; - internal BigInteger[] data; + internal byte[][] data; - internal PubMethod( - PgpPublicKey pubKey) + internal PubMethod(PgpPublicKey pubKey) { this.pubKey = pubKey; } - public override void AddSessionInfo( - byte[] si, + public override void AddSessionInfo( + byte[] sessionInfo, SecureRandom random) { - IBufferedCipher c; + byte[] encryptedSessionInfo = EncryptSessionInfo(sessionInfo, random); + + this.data = ProcessSessionInfo(encryptedSessionInfo); + } - switch (pubKey.Algorithm) + private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random) + { + if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH) { - 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); + 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)); + return c.DoFinal(sessionInfo); } - AsymmetricKeyParameter akp = pubKey.GetKey(); + ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key; + + // Generate the ephemeral key pair + IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH"); + gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random)); + + AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair(); + ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private; + ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public; + + ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey(); + ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize(); + + KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S)); + + IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm); + w.Init(true, new ParametersWithRandom(key, random)); + + byte[] paddedSessionData = PgpPad.PadSessionData(sessionInfo); + + byte[] C = w.Wrap(paddedSessionData, 0, paddedSessionData.Length); + byte[] VB = new MPInteger(new BigInteger(1, ephPub.Q.GetEncoded(false))).GetEncoded(); - c.Init(true, new ParametersWithRandom(akp, random)); + byte[] rv = new byte[VB.Length + 1 + C.Length]; - byte[] encKey = c.DoFinal(si); + Array.Copy(VB, 0, rv, 0, VB.Length); + rv[VB.Length] = (byte)C.Length; + Array.Copy(C, 0, rv, VB.Length + 1, C.Length); - switch (pubKey.Algorithm) + return rv; + } + + private byte[][] ProcessSessionInfo(byte[] encryptedSessionInfo) + { + byte[][] data; + + 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); + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + data = new byte[][] { ConvertToEncodedMpi(encryptedSessionInfo) }; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + int halfLength = encryptedSessionInfo.Length / 2; + byte[] b1 = new byte[halfLength]; + byte[] b2 = new byte[halfLength]; + + Array.Copy(encryptedSessionInfo, 0, b1, 0, halfLength); + Array.Copy(encryptedSessionInfo, halfLength, b2, 0, halfLength); + + data = new byte[][] { + ConvertToEncodedMpi(b1), + ConvertToEncodedMpi(b2), + }; + break; + case PublicKeyAlgorithmTag.ECDH: + data = new byte[][]{ encryptedSessionInfo }; + break; + default: + throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm); } + + return data; } - public override void Encode(BcpgOutputStream pOut) + private byte[] ConvertToEncodedMpi(byte[] encryptedSessionInfo) + { + try + { + return new MPInteger(new BigInteger(1, encryptedSessionInfo)).GetEncoded(); + } + catch (IOException e) + { + throw new PgpException("Invalid MPI encoding: " + e.Message, e); + } + } + + public override void Encode(BcpgOutputStream pOut) { - PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket( - pubKey.KeyId, pubKey.Algorithm, data); + PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(pubKey.KeyId, pubKey.Algorithm, data); - pOut.WritePacket(pk); + pOut.WritePacket(pk); } } diff --git a/crypto/src/openpgp/PgpKeyPair.cs b/crypto/src/openpgp/PgpKeyPair.cs
index 6efb03a42..9cf78fa6f 100644 --- a/crypto/src/openpgp/PgpKeyPair.cs +++ b/crypto/src/openpgp/PgpKeyPair.cs
@@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp DateTime time) { this.pub = new PgpPublicKey(algorithm, pubKey, time); - this.priv = new PgpPrivateKey(privKey, pub.KeyId); + this.priv = new PgpPrivateKey(pub.KeyId, pub.PublicKeyPacket, privKey); } /// <summary>Create a key pair from a PgpPrivateKey and a PgpPublicKey.</summary> diff --git a/crypto/src/openpgp/PgpPad.cs b/crypto/src/openpgp/PgpPad.cs new file mode 100644
index 000000000..48f7f2f44 --- /dev/null +++ b/crypto/src/openpgp/PgpPad.cs
@@ -0,0 +1,45 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Padding functions.</remarks> + public sealed class PgpPad + { + private PgpPad() + { + } + + public static byte[] PadSessionData(byte[] sessionInfo) + { + byte[] result = new byte[40]; + + Array.Copy(sessionInfo, 0, result, 0, sessionInfo.Length); + + byte padValue = (byte)(result.Length - sessionInfo.Length); + + for (int i = sessionInfo.Length; i != result.Length; i++) + { + result[i] = padValue; + } + + return result; + } + + public static byte[] UnpadSessionData(byte[] encoded) + { + byte padValue = encoded[encoded.Length - 1]; + + for (int i = encoded.Length - padValue; i != encoded.Length; i++) + { + if (encoded[i] != padValue) + throw new PgpException("bad padding found in session data"); + } + + byte[] taggedKey = new byte[encoded.Length - padValue]; + + Array.Copy(encoded, 0, taggedKey, 0, taggedKey.Length); + + return taggedKey; + } + } +} diff --git a/crypto/src/openpgp/PgpPrivateKey.cs b/crypto/src/openpgp/PgpPrivateKey.cs
index 154c87cd7..61487a5b2 100644 --- a/crypto/src/openpgp/PgpPrivateKey.cs +++ b/crypto/src/openpgp/PgpPrivateKey.cs
@@ -7,33 +7,42 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <remarks>General class to contain a private key for use with other OpenPGP objects.</remarks> public class PgpPrivateKey { - private readonly long keyId; + private readonly long keyID; + private readonly PublicKeyPacket publicKeyPacket; private readonly AsymmetricKeyParameter privateKey; - /// <summary> - /// Create a PgpPrivateKey from a regular private key and the ID of its - /// associated public key. + /// <summary> + /// Create a PgpPrivateKey from a keyID, the associated public data packet, and a regular private key. /// </summary> - /// <param name="privateKey">Private key to use.</param> - /// <param name="keyId">ID of the corresponding public key.</param> - public PgpPrivateKey( - AsymmetricKeyParameter privateKey, - long keyId) + /// <param name="keyID">ID of the corresponding public key.</param> + /// <param name="publicKeyPacket">the public key data packet to be associated with this private key.</param> + /// <param name="privateKey">the private key data packet to be associated with this private key.</param> + public PgpPrivateKey( + long keyID, + PublicKeyPacket publicKeyPacket, + AsymmetricKeyParameter privateKey) { if (!privateKey.IsPrivate) throw new ArgumentException("Expected a private key", "privateKey"); - this.privateKey = privateKey; - this.keyId = keyId; + this.keyID = keyID; + this.publicKeyPacket = publicKeyPacket; + this.privateKey = privateKey; } - /// <summary>The keyId associated with the contained private key.</summary> + /// <summary>The keyId associated with the contained private key.</summary> public long KeyId { - get { return keyId; } + get { return keyID; } } - /// <summary>The contained private key.</summary> + /// <summary>The public key packet associated with this private key, if available.</summary> + public PublicKeyPacket PublicKeyPacket + { + get { return publicKeyPacket; } + } + + /// <summary>The contained private key.</summary> public AsymmetricKeyParameter Key { get { return privateKey; } diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index b0720146c..904e29913 100644 --- a/crypto/src/openpgp/PgpPublicKey.cs +++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -2,88 +2,107 @@ using System; using System.Collections; using System.IO; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.IO; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { - /// <remarks>General class to handle a PGP public key object.</remarks> + /// <remarks>General class to handle a PGP public key object.</remarks> 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; - internal TrustPacket trustPk; - internal IList keySigs = Platform.CreateArrayList(); - internal IList ids = Platform.CreateArrayList(); - internal IList idTrusts = Platform.CreateArrayList(); - internal IList idSigs = Platform.CreateArrayList(); - internal IList subSigs; - - private void Init() + public static byte[] CalculateFingerprint(PublicKeyPacket publicPk) { IBcpgKey key = publicPk.Key; + IDigest digest; - if (publicPk.Version <= 3) + if (publicPk.Version <= 3) { - RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key; + RsaPublicBcpgKey rK = (RsaPublicBcpgKey)key; - this.keyId = rK.Modulus.LongValue; - - try + 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); + digest = DigestUtilities.GetDigest("MD5"); + UpdateDigest(digest, rK.Modulus); + UpdateDigest(digest, rK.PublicExponent); } - //catch (NoSuchAlgorithmException) - catch (Exception e) + catch (Exception e) { - throw new IOException("can't find MD5", e); + throw new PgpException("can't encode key components: " + e.Message, e); } - - this.keyStrength = rK.Modulus.BitLength; } else { - byte[] kBytes = publicPk.GetEncodedContents(); - - try + try { - IDigest digest = DigestUtilities.GetDigest("SHA1"); + byte[] kBytes = publicPk.GetEncodedContents(); - digest.Update(0x99); + 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 (Exception e) { - throw new IOException("can't find SHA1", e); + throw new PgpException("can't encode key components: " + e.Message, e); } + } + + return DigestUtilities.DoFinal(digest); + } + + private static void UpdateDigest(IDigest d, BigInteger b) + { + byte[] bytes = b.ToByteArrayUnsigned(); + d.BlockUpdate(bytes, 0, bytes.Length); + } + + private static readonly int[] MasterKeyCertificationTypes = new int[] + { + PgpSignature.PositiveCertification, + PgpSignature.CasualCertification, + PgpSignature.NoCertification, + PgpSignature.DefaultCertification + }; - this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56) + private long keyId; + private byte[] fingerprint; + private int keyStrength; + + internal PublicKeyPacket publicPk; + internal TrustPacket trustPk; + internal IList keySigs = Platform.CreateArrayList(); + internal IList ids = Platform.CreateArrayList(); + internal IList idTrusts = Platform.CreateArrayList(); + internal IList idSigs = Platform.CreateArrayList(); + internal IList subSigs; + + private void Init() + { + IBcpgKey key = publicPk.Key; + + this.fingerprint = CalculateFingerprint(publicPk); + + if (publicPk.Version <= 3) + { + RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key; + + this.keyId = rK.Modulus.LongValue; + this.keyStrength = rK.Modulus.BitLength; + } + else + { + 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) @@ -92,7 +111,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp | ((ulong)fingerprint[fingerprint.Length - 2] << 8) | (ulong)fingerprint[fingerprint.Length - 1]); - if (key is RsaPublicBcpgKey) + if (key is RsaPublicBcpgKey) { this.keyStrength = ((RsaPublicBcpgKey)key).Modulus.BitLength; } @@ -104,60 +123,81 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength; } + else if (key is ECPublicBcpgKey) + { + this.keyStrength = ECKeyPairGenerator.FindECCurveByOid(((ECPublicBcpgKey)key).CurveOid).Curve.FieldSize; + } } } - /// <summary> - /// Create a PgpPublicKey from the passed in lightweight one. - /// </summary> - /// <remarks> - /// 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. - /// </remarks> - /// <param name="algorithm">Asymmetric algorithm type representing the public key.</param> - /// <param name="pubKey">Actual public key to associate.</param> - /// <param name="time">Date of creation.</param> - /// <exception cref="ArgumentException">If <c>pubKey</c> is not public.</exception> - /// <exception cref="PgpException">On key creation problem.</exception> + /// <summary> + /// Create a PgpPublicKey from the passed in lightweight one. + /// </summary> + /// <remarks> + /// 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. + /// </remarks> + /// <param name="algorithm">Asymmetric algorithm type representing the public key.</param> + /// <param name="pubKey">Actual public key to associate.</param> + /// <param name="time">Date of creation.</param> + /// <exception cref="ArgumentException">If <c>pubKey</c> is not public.</exception> + /// <exception cref="PgpException">On key creation problem.</exception> public PgpPublicKey( PublicKeyAlgorithmTag algorithm, AsymmetricKeyParameter pubKey, DateTime time) { - if (pubKey.IsPrivate) - throw new ArgumentException("Expected a public key", "pubKey"); + if (pubKey.IsPrivate) + throw new ArgumentException("Expected a public key", "pubKey"); - IBcpgKey bcpgKey; + IBcpgKey bcpgKey; if (pubKey is RsaKeyParameters) { RsaKeyParameters rK = (RsaKeyParameters) pubKey; - bcpgKey = new RsaPublicBcpgKey(rK.Modulus, rK.Exponent); + 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); + bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y); + } + else if (pubKey is ECPublicKeyParameters) + { + ECPublicKeyParameters ecK = (ECPublicKeyParameters)pubKey; + + if (algorithm == PublicKeyAlgorithmTag.ECDH) + { + bcpgKey = new ECDHPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q, HashAlgorithmTag.Sha256, SymmetricKeyAlgorithmTag.Aes128); + } + else if (algorithm == PublicKeyAlgorithmTag.ECDsa) + { + bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q); + } + else + { + throw new PgpException("unknown EC algorithm"); + } } else if (pubKey is ElGamalPublicKeyParameters) { ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey; ElGamalParameters eS = eK.Parameters; - bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y); + bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y); } else { throw new PgpException("unknown key class"); } - this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey); + this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey); this.ids = Platform.CreateArrayList(); this.idSigs = Platform.CreateArrayList(); - try + try { Init(); } @@ -167,7 +207,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - /// <summary>Constructor for a sub-key.</summary> + public PgpPublicKey(PublicKeyPacket publicPk) + : this(publicPk, Platform.CreateArrayList(), Platform.CreateArrayList()) + { + } + + /// <summary>Constructor for a sub-key.</summary> internal PgpPublicKey( PublicKeyPacket publicPk, TrustPacket trustPk, @@ -177,10 +222,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp this.trustPk = trustPk; this.subSigs = sigs; - Init(); + Init(); } - internal PgpPublicKey( + internal PgpPublicKey( PgpPublicKey key, TrustPacket trust, IList subSigs) @@ -189,19 +234,19 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp this.trustPk = trust; this.subSigs = subSigs; - this.fingerprint = key.fingerprint; + this.fingerprint = key.fingerprint; this.keyId = key.keyId; this.keyStrength = key.keyStrength; } - /// <summary>Copy constructor.</summary> - /// <param name="pubKey">The public key to copy.</param> + /// <summary>Copy constructor.</summary> + /// <param name="pubKey">The public key to copy.</param> internal PgpPublicKey( PgpPublicKey pubKey) { this.publicPk = pubKey.publicPk; - this.keySigs = Platform.CreateArrayList(pubKey.keySigs); + this.keySigs = Platform.CreateArrayList(pubKey.keySigs); this.ids = Platform.CreateArrayList(pubKey.ids); this.idTrusts = Platform.CreateArrayList(pubKey.idTrusts); this.idSigs = Platform.CreateArrayList(pubKey.idSigs.Count); @@ -210,7 +255,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp this.idSigs.Add(Platform.CreateArrayList((IList)pubKey.idSigs[i])); } - if (pubKey.subSigs != null) + if (pubKey.subSigs != null) { this.subSigs = Platform.CreateArrayList(pubKey.subSigs.Count); for (int i = 0; i != pubKey.subSigs.Count; i++) @@ -219,12 +264,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - this.fingerprint = pubKey.fingerprint; + this.fingerprint = pubKey.fingerprint; this.keyId = pubKey.keyId; this.keyStrength = pubKey.keyStrength; } - internal PgpPublicKey( + internal PgpPublicKey( PublicKeyPacket publicPk, TrustPacket trustPk, IList keySigs, @@ -239,10 +284,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp this.idTrusts = idTrusts; this.idSigs = idSigs; - Init(); + Init(); } - internal PgpPublicKey( + internal PgpPublicKey( PublicKeyPacket publicPk, IList ids, IList idSigs) @@ -253,159 +298,165 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp Init(); } - /// <summary>The version of this key.</summary> + /// <summary>The version of this key.</summary> public int Version { - get { return publicPk.Version; } + get { return publicPk.Version; } } - /// <summary>The creation time of this key.</summary> - public DateTime CreationTime + /// <summary>The creation time of this key.</summary> + public DateTime CreationTime { - get { return publicPk.GetTime(); } + get { return publicPk.GetTime(); } } - /// <summary>The number of valid days from creation time - zero means no expiry.</summary> + /// <summary>The number of valid days from creation time - zero means no expiry.</summary> + /// <remarks>WARNING: This method will return 1 for keys with version > 3 that expire in less than 1 day</remarks> + [Obsolete("Use 'GetValidSeconds' instead")] public int ValidDays { - get - { - if (publicPk.Version > 3) - { - return (int)(GetValidSeconds() / (24 * 60 * 60)); - } - - return publicPk.ValidDays; - } - } - - /// <summary>Return the trust data associated with the public key, if present.</summary> - /// <returns>A byte array with trust data, null otherwise.</returns> - public byte[] GetTrustData() - { - if (trustPk == null) - { - return null; - } - - return trustPk.GetLevelAndTrustAmount(); - } - - /// <summary>The number of valid seconds from creation time - zero means no expiry.</summary> - 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; - } - - /// <summary>The keyId associated with the public key.</summary> + get + { + if (publicPk.Version <= 3) + { + return publicPk.ValidDays; + } + + long expSecs = GetValidSeconds(); + if (expSecs <= 0) + return 0; + + int days = (int)(expSecs / (24 * 60 * 60)); + return System.Math.Max(1, days); + } + } + + /// <summary>Return the trust data associated with the public key, if present.</summary> + /// <returns>A byte array with trust data, null otherwise.</returns> + public byte[] GetTrustData() + { + if (trustPk == null) + { + return null; + } + + return Arrays.Clone(trustPk.GetLevelAndTrustAmount()); + } + + /// <summary>The number of valid seconds from creation time - zero means no expiry.</summary> + public long GetValidSeconds() + { + if (publicPk.Version <= 3) + { + return (long)publicPk.ValidDays * (24 * 60 * 60); + } + + 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; + } + + 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; + } + + /// <summary>The keyId associated with the public key.</summary> public long KeyId { get { return keyId; } } - /// <summary>The fingerprint of the key</summary> + /// <summary>The fingerprint of the key</summary> public byte[] GetFingerprint() { - return (byte[]) fingerprint.Clone(); + return (byte[]) fingerprint.Clone(); } - /// <summary> - /// Check if this key has an algorithm type that makes it suitable to use for encryption. - /// </summary> - /// <remarks> - /// Note: with version 4 keys KeyFlags subpackets should also be considered when present for - /// determining the preferred use of the key. - /// </remarks> - /// <returns> - /// <c>true</c> if this key algorithm is suitable for encryption. - /// </returns> - public bool IsEncryptionKey + /// <summary> + /// Check if this key has an algorithm type that makes it suitable to use for encryption. + /// </summary> + /// <remarks> + /// Note: with version 4 keys KeyFlags subpackets should also be considered when present for + /// determining the preferred use of the key. + /// </remarks> + /// <returns> + /// <c>true</c> if this key algorithm is suitable for encryption. + /// </returns> + public bool IsEncryptionKey { get { - switch (publicPk.Algorithm) - { - case PublicKeyAlgorithmTag.ElGamalEncrypt: - case PublicKeyAlgorithmTag.ElGamalGeneral: - case PublicKeyAlgorithmTag.RsaEncrypt: - case PublicKeyAlgorithmTag.RsaGeneral: - return true; - default: - return false; - } + switch (publicPk.Algorithm) + { + case PublicKeyAlgorithmTag.ECDH: + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + return true; + default: + return false; + } } } - /// <summary>True, if this is a master key.</summary> + /// <summary>True, if this is a master key.</summary> public bool IsMasterKey { get { return subSigs == null; } } - /// <summary>The algorithm code associated with the public key.</summary> + /// <summary>The algorithm code associated with the public key.</summary> public PublicKeyAlgorithmTag Algorithm { - get { return publicPk.Algorithm; } + get { return publicPk.Algorithm; } } - /// <summary>The strength of the key in bits.</summary> + /// <summary>The strength of the key in bits.</summary> public int BitStrength { get { return keyStrength; } } - /// <summary>The public key contained in the object.</summary> - /// <returns>A lightweight public key.</returns> - /// <exception cref="PgpException">If the key algorithm is not recognised.</exception> + /// <summary>The public key contained in the object.</summary> + /// <returns>A lightweight public key.</returns> + /// <exception cref="PgpException">If the key algorithm is not recognised.</exception> public AsymmetricKeyParameter GetKey() { try @@ -415,14 +466,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp case PublicKeyAlgorithmTag.RsaEncrypt: case PublicKeyAlgorithmTag.RsaGeneral: case PublicKeyAlgorithmTag.RsaSign: - RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey) publicPk.Key; + RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key; return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent); case PublicKeyAlgorithmTag.Dsa: - DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey) publicPk.Key; + DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key; return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G)); + case PublicKeyAlgorithmTag.ECDsa: + return GetECKey("ECDSA"); + case PublicKeyAlgorithmTag.ECDH: + return GetECKey("ECDH"); case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: - ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey) publicPk.Key; + 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"); @@ -438,50 +493,58 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - /// <summary>Allows enumeration of any user IDs associated with the key.</summary> - /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns> + private ECPublicKeyParameters GetECKey(string algorithm) + { + ECPublicBcpgKey ecK = (ECPublicBcpgKey)publicPk.Key; + X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(ecK.CurveOid); + ECPoint q = x9.Curve.DecodePoint(BigIntegers.AsUnsignedByteArray(ecK.EncodedPoint)); + return new ECPublicKeyParameters(algorithm, q, ecK.CurveOid); + } + + /// <summary>Allows enumeration of any user IDs associated with the key.</summary> + /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns> public IEnumerable GetUserIds() { IList temp = Platform.CreateArrayList(); - foreach (object o in ids) - { - if (o is string) - { - temp.Add(o); + foreach (object o in ids) + { + if (o is string) + { + temp.Add(o); } } - return new EnumerableProxy(temp); + return new EnumerableProxy(temp); } - /// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary> - /// <returns>An <c>IEnumerable</c> of <c>PgpUserAttributeSubpacketVector</c> objects.</returns> + /// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpUserAttributeSubpacketVector</c> objects.</returns> public IEnumerable GetUserAttributes() { IList temp = Platform.CreateArrayList(); - foreach (object o in ids) - { - if (o is PgpUserAttributeSubpacketVector) - { - temp.Add(o); - } - } + foreach (object o in ids) + { + if (o is PgpUserAttributeSubpacketVector) + { + temp.Add(o); + } + } - return new EnumerableProxy(temp); + return new EnumerableProxy(temp); } - /// <summary>Allows enumeration of any signatures associated with the passed in id.</summary> - /// <param name="id">The ID to be matched.</param> - /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> + /// <summary>Allows enumeration of any signatures associated with the passed in id.</summary> + /// <param name="id">The ID to be matched.</param> + /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> public IEnumerable GetSignaturesForId( string id) { - if (id == null) - throw new ArgumentNullException("id"); + if (id == null) + throw new ArgumentNullException("id"); - for (int i = 0; i != ids.Count; i++) + for (int i = 0; i != ids.Count; i++) { if (id.Equals(ids[i])) { @@ -489,12 +552,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - return null; + return null; } - /// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary> - /// <param name="userAttributes">The vector of user attributes to be matched.</param> - /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> + /// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary> + /// <param name="userAttributes">The vector of user attributes to be matched.</param> + /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> public IEnumerable GetSignaturesForUserAttribute( PgpUserAttributeSubpacketVector userAttributes) { @@ -506,18 +569,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - return null; + return null; } - /// <summary>Allows enumeration of signatures of the passed in type that are on this key.</summary> - /// <param name="signatureType">The type of the signature to be returned.</param> - /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> + /// <summary>Allows enumeration of signatures of the passed in type that are on this key.</summary> + /// <param name="signatureType">The type of the signature to be returned.</param> + /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> public IEnumerable GetSignaturesOfType( int signatureType) { IList temp = Platform.CreateArrayList(); - foreach (PgpSignature sig in GetSignatures()) + foreach (PgpSignature sig in GetSignatures()) { if (sig.SignatureType == signatureType) { @@ -525,63 +588,79 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - return new EnumerableProxy(temp); + return new EnumerableProxy(temp); } - /// <summary>Allows enumeration of all signatures/certifications associated with this key.</summary> - /// <returns>An <c>IEnumerable</c> with all signatures/certifications.</returns> + /// <summary>Allows enumeration of all signatures/certifications associated with this key.</summary> + /// <returns>An <c>IEnumerable</c> with all signatures/certifications.</returns> public IEnumerable GetSignatures() { - IList sigs; - if (subSigs != null) - { - sigs = subSigs; - } - else - { + IList sigs = subSigs; + if (sigs == null) + { sigs = Platform.CreateArrayList(keySigs); - foreach (ICollection extraSigs in idSigs) - { + foreach (ICollection extraSigs in idSigs) + { CollectionUtilities.AddRange(sigs, extraSigs); - } - } + } + } + + return new EnumerableProxy(sigs); + } - return new EnumerableProxy(sigs); + /** + * Return all signatures/certifications directly associated with this key (ie, not to a user id). + * + * @return an iterator (possibly empty) with all signatures/certifications. + */ + public IEnumerable GetKeySignatures() + { + IList sigs = subSigs; + if (sigs == null) + { + sigs = Platform.CreateArrayList(keySigs); + } + return new EnumerableProxy(sigs); } - public byte[] GetEncoded() + public PublicKeyPacket PublicKeyPacket + { + get { return publicPk; } + } + + public byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); Encode(bOut); return bOut.ToArray(); } - public void Encode( + public void Encode( Stream outStr) { BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); - bcpgOut.WritePacket(publicPk); + bcpgOut.WritePacket(publicPk); if (trustPk != null) { bcpgOut.WritePacket(trustPk); } - if (subSigs == null) // not a sub-key + if (subSigs == null) // not a sub-key { - foreach (PgpSignature keySig in keySigs) - { - keySig.Encode(bcpgOut); - } + foreach (PgpSignature keySig in keySigs) + { + keySig.Encode(bcpgOut); + } - for (int i = 0; i != ids.Count; i++) + for (int i = 0; i != ids.Count; i++) { if (ids[i] is string) { string id = (string) ids[i]; - bcpgOut.WritePacket(new UserIdPacket(id)); + bcpgOut.WritePacket(new UserIdPacket(id)); } else { @@ -589,28 +668,28 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); } - if (idTrusts[i] != null) + if (idTrusts[i] != null) { bcpgOut.WritePacket((ContainedPacket)idTrusts[i]); } - foreach (PgpSignature sig in (IList) idSigs[i]) - { - sig.Encode(bcpgOut); - } + foreach (PgpSignature sig in (IList) idSigs[i]) + { + sig.Encode(bcpgOut); + } } } else { - foreach (PgpSignature subSig in subSigs) - { - subSig.Encode(bcpgOut); - } + foreach (PgpSignature subSig in subSigs) + { + subSig.Encode(bcpgOut); + } } } - /// <summary>Check whether this (sub)key has a revocation signature on it.</summary> - /// <returns>True, if this (sub)key has been revoked.</returns> + /// <summary>Check whether this (sub)key has a revocation signature on it.</summary> + /// <returns>True, if this (sub)key has been revoked.</returns> public bool IsRevoked() { int ns = 0; @@ -638,98 +717,98 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return revoked; } - /// <summary>Add a certification for an id to the given public key.</summary> - /// <param name="key">The key the certification is to be added to.</param> - /// <param name="id">The ID the certification is associated with.</param> - /// <param name="certification">The new certification.</param> - /// <returns>The re-certified key.</returns> + /// <summary>Add a certification for an id to the given public key.</summary> + /// <param name="key">The key the certification is to be added to.</param> + /// <param name="id">The ID the certification is associated with.</param> + /// <param name="certification">The new certification.</param> + /// <returns>The re-certified key.</returns> public static PgpPublicKey AddCertification( PgpPublicKey key, string id, PgpSignature certification) { - return AddCert(key, id, certification); - } - - /// <summary>Add a certification for the given UserAttributeSubpackets to the given public key.</summary> - /// <param name="key">The key the certification is to be added to.</param> - /// <param name="userAttributes">The attributes the certification is associated with.</param> - /// <param name="certification">The new certification.</param> - /// <returns>The re-certified key.</returns> - public static PgpPublicKey AddCertification( - PgpPublicKey key, - PgpUserAttributeSubpacketVector userAttributes, - PgpSignature certification) - { - return AddCert(key, userAttributes, certification); - } - - private static PgpPublicKey AddCert( - PgpPublicKey key, - object id, - PgpSignature certification) - { - PgpPublicKey returnKey = new PgpPublicKey(key); - IList sigList = null; - - for (int i = 0; i != returnKey.ids.Count; i++) - { - if (id.Equals(returnKey.ids[i])) - { - sigList = (IList) returnKey.idSigs[i]; - } - } - - if (sigList != null) - { - sigList.Add(certification); - } - else - { - sigList = Platform.CreateArrayList(); - sigList.Add(certification); - returnKey.ids.Add(id); - returnKey.idTrusts.Add(null); - returnKey.idSigs.Add(sigList); - } - - return returnKey; - } - - /// <summary> - /// Remove any certifications associated with a user attribute subpacket on a key. - /// </summary> - /// <param name="key">The key the certifications are to be removed from.</param> - /// <param name="userAttributes">The attributes to be removed.</param> - /// <returns> - /// The re-certified key, or null if the user attribute subpacket was not found on the key. - /// </returns> - public static PgpPublicKey RemoveCertification( - PgpPublicKey key, - PgpUserAttributeSubpacketVector userAttributes) - { - return RemoveCert(key, userAttributes); - } - - /// <summary>Remove any certifications associated with a given ID on a key.</summary> - /// <param name="key">The key the certifications are to be removed from.</param> - /// <param name="id">The ID that is to be removed.</param> - /// <returns>The re-certified key, or null if the ID was not found on the key.</returns> + return AddCert(key, id, certification); + } + + /// <summary>Add a certification for the given UserAttributeSubpackets to the given public key.</summary> + /// <param name="key">The key the certification is to be added to.</param> + /// <param name="userAttributes">The attributes the certification is associated with.</param> + /// <param name="certification">The new certification.</param> + /// <returns>The re-certified key.</returns> + public static PgpPublicKey AddCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes, + PgpSignature certification) + { + return AddCert(key, userAttributes, certification); + } + + private static PgpPublicKey AddCert( + PgpPublicKey key, + object id, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + IList sigList = null; + + for (int i = 0; i != returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + sigList = (IList) returnKey.idSigs[i]; + } + } + + if (sigList != null) + { + sigList.Add(certification); + } + else + { + sigList = Platform.CreateArrayList(); + sigList.Add(certification); + returnKey.ids.Add(id); + returnKey.idTrusts.Add(null); + returnKey.idSigs.Add(sigList); + } + + return returnKey; + } + + /// <summary> + /// Remove any certifications associated with a user attribute subpacket on a key. + /// </summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="userAttributes">The attributes to be removed.</param> + /// <returns> + /// The re-certified key, or null if the user attribute subpacket was not found on the key. + /// </returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes) + { + return RemoveCert(key, userAttributes); + } + + /// <summary>Remove any certifications associated with a given ID on a key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="id">The ID that is to be removed.</param> + /// <returns>The re-certified key, or null if the ID was not found on the key.</returns> public static PgpPublicKey RemoveCertification( PgpPublicKey key, string id) { - return RemoveCert(key, id); - } + return RemoveCert(key, id); + } - private static PgpPublicKey RemoveCert( - PgpPublicKey key, - object id) - { - PgpPublicKey returnKey = new PgpPublicKey(key); + private static PgpPublicKey RemoveCert( + PgpPublicKey key, + object id) + { + PgpPublicKey returnKey = new PgpPublicKey(key); bool found = false; - for (int i = 0; i < returnKey.ids.Count; i++) + for (int i = 0; i < returnKey.ids.Count; i++) { if (id.Equals(returnKey.ids[i])) { @@ -740,64 +819,64 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - return found ? returnKey : null; + return found ? returnKey : null; } - /// <summary>Remove a certification associated with a given ID on a key.</summary> - /// <param name="key">The key the certifications are to be removed from.</param> - /// <param name="id">The ID that the certfication is to be removed from.</param> - /// <param name="certification">The certfication to be removed.</param> - /// <returns>The re-certified key, or null if the certification was not found.</returns> + /// <summary>Remove a certification associated with a given ID on a key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="id">The ID that the certfication is to be removed from.</param> + /// <param name="certification">The certfication to be removed.</param> + /// <returns>The re-certified key, or null if the certification was not found.</returns> public static PgpPublicKey RemoveCertification( PgpPublicKey key, string id, PgpSignature certification) { - return RemoveCert(key, id, certification); - } - - /// <summary>Remove a certification associated with a given user attributes on a key.</summary> - /// <param name="key">The key the certifications are to be removed from.</param> - /// <param name="userAttributes">The user attributes that the certfication is to be removed from.</param> - /// <param name="certification">The certification to be removed.</param> - /// <returns>The re-certified key, or null if the certification was not found.</returns> - public static PgpPublicKey RemoveCertification( - PgpPublicKey key, - PgpUserAttributeSubpacketVector userAttributes, - PgpSignature certification) - { - return RemoveCert(key, userAttributes, certification); - } - - private static PgpPublicKey RemoveCert( - PgpPublicKey key, - object id, - PgpSignature certification) - { - PgpPublicKey returnKey = new PgpPublicKey(key); + return RemoveCert(key, id, certification); + } + + /// <summary>Remove a certification associated with a given user attributes on a key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="userAttributes">The user attributes that the certfication is to be removed from.</param> + /// <param name="certification">The certification to be removed.</param> + /// <returns>The re-certified key, or null if the certification was not found.</returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes, + PgpSignature certification) + { + return RemoveCert(key, userAttributes, certification); + } + + private static PgpPublicKey RemoveCert( + PgpPublicKey key, + object id, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); bool found = false; - for (int i = 0; i < returnKey.ids.Count; i++) + for (int i = 0; i < returnKey.ids.Count; i++) { if (id.Equals(returnKey.ids[i])) { IList certs = (IList) returnKey.idSigs[i]; found = certs.Contains(certification); - if (found) - { - certs.Remove(certification); - } + if (found) + { + certs.Remove(certification); + } } } - return found ? returnKey : null; + return found ? returnKey : null; } - /// <summary>Add a revocation or some other key certification to a key.</summary> - /// <param name="key">The key the revocation is to be added to.</param> - /// <param name="certification">The key signature to be added.</param> - /// <returns>The new changed public key object.</returns> + /// <summary>Add a revocation or some other key certification to a key.</summary> + /// <param name="key">The key the revocation is to be added to.</param> + /// <param name="certification">The key signature to be added.</param> + /// <returns>The new changed public key object.</returns> public static PgpPublicKey AddCertification( PgpPublicKey key, PgpSignature certification) @@ -817,9 +896,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - PgpPublicKey returnKey = new PgpPublicKey(key); + PgpPublicKey returnKey = new PgpPublicKey(key); - if (returnKey.subSigs != null) + if (returnKey.subSigs != null) { returnKey.subSigs.Add(certification); } @@ -828,63 +907,63 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp returnKey.keySigs.Add(certification); } - return returnKey; + return returnKey; } - /// <summary>Remove a certification from the key.</summary> - /// <param name="key">The key the certifications are to be removed from.</param> - /// <param name="certification">The certfication to be removed.</param> - /// <returns>The modified key, null if the certification was not found.</returns> - public static PgpPublicKey RemoveCertification( - PgpPublicKey key, - PgpSignature certification) - { - PgpPublicKey returnKey = new PgpPublicKey(key); - IList sigs = returnKey.subSigs != null - ? returnKey.subSigs - : returnKey.keySigs; + /// <summary>Remove a certification from the key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="certification">The certfication to be removed.</param> + /// <returns>The modified key, null if the certification was not found.</returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + IList sigs = returnKey.subSigs != null + ? returnKey.subSigs + : returnKey.keySigs; // bool found = sigs.Remove(certification); - int pos = sigs.IndexOf(certification); - bool found = pos >= 0; - - if (found) - { - sigs.RemoveAt(pos); - } - else - { - foreach (String id in key.GetUserIds()) - { - foreach (object sig in key.GetSignaturesForId(id)) - { - // TODO Is this the right type of equality test? - if (certification == sig) - { - found = true; - returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification); - } - } - } - - if (!found) - { - foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes()) - { - foreach (object sig in key.GetSignaturesForUserAttribute(id)) - { - // TODO Is this the right type of equality test? - if (certification == sig) - { - found = true; - returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification); - } - } - } - } - } - - return returnKey; - } - } + int pos = sigs.IndexOf(certification); + bool found = pos >= 0; + + if (found) + { + sigs.RemoveAt(pos); + } + else + { + foreach (String id in key.GetUserIds()) + { + foreach (object sig in key.GetSignaturesForId(id)) + { + // TODO Is this the right type of equality test? + if (certification == sig) + { + found = true; + returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification); + } + } + } + + if (!found) + { + foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes()) + { + foreach (object sig in key.GetSignaturesForUserAttribute(id)) + { + // TODO Is this the right type of equality test? + if (certification == sig) + { + found = true; + returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification); + } + } + } + } + } + + return returnKey; + } + } } diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
index b6504cbcd..c2a351182 100644 --- a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs +++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -1,10 +1,13 @@ using System; using System.IO; +using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.IO; @@ -77,22 +80,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp public SymmetricKeyAlgorithmTag GetSymmetricAlgorithm( PgpPrivateKey privKey) { - byte[] plain = fetchSymmetricKeyData(privKey); + byte[] sessionData = RecoverSessionData(privKey); - return (SymmetricKeyAlgorithmTag) plain[0]; + return (SymmetricKeyAlgorithmTag)sessionData[0]; } - /// <summary>Return the decrypted data stream for the packet.</summary> + /// <summary>Return the decrypted data stream for the packet.</summary> public Stream GetDataStream( PgpPrivateKey privKey) { - byte[] plain = fetchSymmetricKeyData(privKey); + byte[] sessionData = RecoverSessionData(privKey); - IBufferedCipher c2; - string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]); + if (!ConfirmCheckSum(sessionData)) + throw new PgpKeyValidationException("key checksum failed"); + + SymmetricKeyAlgorithmTag symmAlg = (SymmetricKeyAlgorithmTag)sessionData[0]; + if (symmAlg == SymmetricKeyAlgorithmTag.Null) + return encData.GetInputStream(); + + IBufferedCipher cipher; + string cipherName = PgpUtilities.GetSymmetricCipherName(symmAlg); string cName = cipherName; - try + try { if (encData is SymmetricEncIntegrityPacket) { @@ -103,7 +113,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp cName += "/OpenPGPCFB/NoPadding"; } - c2 = CipherUtilities.GetCipher(cName); + cipher = CipherUtilities.GetCipher(cName); } catch (PgpException e) { @@ -114,19 +124,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp throw new PgpException("exception creating cipher", e); } - if (c2 == null) - return encData.GetInputStream(); - - try + try { KeyParameter key = ParameterUtilities.CreateKeyParameter( - cipherName, plain, 1, plain.Length - 3); + cipherName, sessionData, 1, sessionData.Length - 3); - byte[] iv = new byte[c2.GetBlockSize()]; + byte[] iv = new byte[cipher.GetBlockSize()]; - c2.Init(false, new ParametersWithIV(key, iv)); + cipher.Init(false, new ParametersWithIV(key, iv)); - encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null)); + encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), cipher, null)); if (encData is SymmetricEncIntegrityPacket) { @@ -178,75 +185,88 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - private byte[] fetchSymmetricKeyData( - PgpPrivateKey privKey) + private byte[] RecoverSessionData(PgpPrivateKey privKey) { - IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm); + byte[][] secKeyData = keyData.GetEncSessionKey(); + + if (keyData.Algorithm == PublicKeyAlgorithmTag.ECDH) + { + ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)privKey.PublicKeyPacket.Key; + X9ECParameters x9Params = ECKeyPairGenerator.FindECCurveByOid(ecKey.CurveOid); + + byte[] enc = secKeyData[0]; + + int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8; + byte[] pEnc = new byte[pLen]; + + Array.Copy(enc, 2, pEnc, 0, pLen); + + byte[] keyEnc = new byte[enc[pLen + 2]]; + + Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length); + + ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc); + + ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key; + ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize(); + + KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S)); + + IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm); + w.Init(false, key); - try + return PgpPad.UnpadSessionData(w.Unwrap(keyEnc, 0, keyEnc.Length)); + } + + IBufferedCipher cipher = GetKeyCipher(keyData.Algorithm); + + try { - c1.Init(false, privKey.Key); + cipher.Init(false, privKey.Key); } catch (InvalidKeyException e) { throw new PgpException("error setting asymmetric cipher", e); } - BigInteger[] keyD = keyData.GetEncSessionKey(); - - if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt + if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt || keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral) { - c1.ProcessBytes(keyD[0].ToByteArrayUnsigned()); + byte[] bi = secKeyData[0]; + + cipher.ProcessBytes(bi, 2, bi.Length - 2); } 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); - } + ProcessEncodedMpi(cipher, size, secKeyData[0]); + ProcessEncodedMpi(cipher, size, secKeyData[1]); } - byte[] plain; - try + try { - plain = c1.DoFinal(); + return cipher.DoFinal(); } catch (Exception e) { throw new PgpException("exception decrypting secret key", e); } - - if (!ConfirmCheckSum(plain)) - throw new PgpKeyValidationException("key checksum failed"); - - return plain; } + + private static void ProcessEncodedMpi(IBufferedCipher cipher, int size, byte[] mpiEnc) + { + if (mpiEnc.Length - 2 > size) // leading Zero? Shouldn't happen but... + { + cipher.ProcessBytes(mpiEnc, 3, mpiEnc.Length - 3); + } + else + { + byte[] tmp = new byte[size]; + Array.Copy(mpiEnc, 2, tmp, tmp.Length - (mpiEnc.Length - 2), mpiEnc.Length - 2); + cipher.ProcessBytes(tmp, 0, tmp.Length); + } + } } } diff --git a/crypto/src/openpgp/PgpPublicKeyRing.cs b/crypto/src/openpgp/PgpPublicKeyRing.cs
index 0f5e04516..592ca86c8 100644 --- a/crypto/src/openpgp/PgpPublicKeyRing.cs +++ b/crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -7,164 +7,164 @@ using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { - /// <remarks> - /// Class to hold a single master public key and its subkeys. - /// <p> - /// Often PGP keyring files consist of multiple master keys, if you are trying to process - /// or construct one of these you should use the <c>PgpPublicKeyRingBundle</c> class. - /// </p> - /// </remarks> - public class PgpPublicKeyRing - : PgpKeyRing + /// <remarks> + /// Class to hold a single master public key and its subkeys. + /// <p> + /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the <c>PgpPublicKeyRingBundle</c> class. + /// </p> + /// </remarks> + public class PgpPublicKeyRing + : PgpKeyRing { private readonly IList keys; - public PgpPublicKeyRing( + public PgpPublicKeyRing( byte[] encoding) : this(new MemoryStream(encoding, false)) { } - internal PgpPublicKeyRing( + internal PgpPublicKeyRing( IList pubKeys) { this.keys = pubKeys; } - public PgpPublicKeyRing( + public PgpPublicKeyRing( Stream inputStream) { - this.keys = Platform.CreateArrayList(); + this.keys = Platform.CreateArrayList(); BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); - PacketTag initialTag = bcpgInput.NextPacketTag(); + 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")); + + "tag 0x" + ((int)initialTag).ToString("X")); } - PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();; - TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput); + PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();; + TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput); // direct signatures and revocations - IList keySigs = ReadSignaturesAndTrust(bcpgInput); + IList keySigs = ReadSignaturesAndTrust(bcpgInput); - IList ids, idTrusts, idSigs; - ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + IList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); - keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs)); + keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs)); - // Read subkeys - while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) { - keys.Add(ReadSubkey(bcpgInput)); + keys.Add(ReadSubkey(bcpgInput)); } } - /// <summary>Return the first public key in the ring.</summary> - public PgpPublicKey GetPublicKey() + /// <summary>Return the first public key in the ring.</summary> + public virtual PgpPublicKey GetPublicKey() { return (PgpPublicKey) keys[0]; } - /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary> - public PgpPublicKey GetPublicKey( + /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary> + public virtual PgpPublicKey GetPublicKey( long keyId) { - foreach (PgpPublicKey k in keys) - { - if (keyId == k.KeyId) + foreach (PgpPublicKey k in keys) + { + if (keyId == k.KeyId) { return k; } } - return null; + return null; } - /// <summary>Allows enumeration of all the public keys.</summary> - /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns> - public IEnumerable GetPublicKeys() + /// <summary>Allows enumeration of all the public keys.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns> + public virtual IEnumerable GetPublicKeys() { return new EnumerableProxy(keys); } - public byte[] GetEncoded() + public virtual byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); - Encode(bOut); + Encode(bOut); - return bOut.ToArray(); + return bOut.ToArray(); } - public void Encode( + public virtual void Encode( Stream outStr) { - if (outStr == null) - throw new ArgumentNullException("outStr"); + if (outStr == null) + throw new ArgumentNullException("outStr"); - foreach (PgpPublicKey k in keys) - { - k.Encode(outStr); + foreach (PgpPublicKey k in keys) + { + k.Encode(outStr); } } - /// <summary> - /// Returns a new key ring with the public key passed in either added or - /// replacing an existing one. - /// </summary> - /// <param name="pubRing">The public key ring to be modified.</param> - /// <param name="pubKey">The public key to be inserted.</param> - /// <returns>A new <c>PgpPublicKeyRing</c></returns> + /// <summary> + /// Returns a new key ring with the public key passed in either added or + /// replacing an existing one. + /// </summary> + /// <param name="pubRing">The public key ring to be modified.</param> + /// <param name="pubKey">The public key to be inserted.</param> + /// <returns>A new <c>PgpPublicKeyRing</c></returns> public static PgpPublicKeyRing InsertPublicKey( PgpPublicKeyRing pubRing, PgpPublicKey pubKey) { IList keys = Platform.CreateArrayList(pubRing.keys); bool found = false; - bool masterFound = false; + bool masterFound = false; - for (int i = 0; i != keys.Count; i++) + for (int i = 0; i != keys.Count; i++) { PgpPublicKey key = (PgpPublicKey) keys[i]; - if (key.KeyId == pubKey.KeyId) + if (key.KeyId == pubKey.KeyId) { found = true; keys[i] = pubKey; } - if (key.IsMasterKey) - { - masterFound = true; - } - } + if (key.IsMasterKey) + { + masterFound = true; + } + } - if (!found) + if (!found) { - if (pubKey.IsMasterKey) - { - if (masterFound) - throw new ArgumentException("cannot add a master key to a ring that already has one"); - - keys.Insert(0, pubKey); - } - else - { - keys.Add(pubKey); - } - } - - return new PgpPublicKeyRing(keys); + if (pubKey.IsMasterKey) + { + if (masterFound) + throw new ArgumentException("cannot add a master key to a ring that already has one"); + + keys.Insert(0, pubKey); + } + else + { + keys.Add(pubKey); + } + } + + return new PgpPublicKeyRing(keys); } - /// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary> - /// <param name="pubRing">The public key ring to be modified.</param> - /// <param name="pubKey">The public key to be removed.</param> - /// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns> + /// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary> + /// <param name="pubRing">The public key ring to be modified.</param> + /// <param name="pubKey">The public key to be removed.</param> + /// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns> public static PgpPublicKeyRing RemovePublicKey( PgpPublicKeyRing pubRing, PgpPublicKey pubKey) @@ -173,29 +173,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp IList keys = Platform.CreateArrayList(pubRing.keys); bool found = false; - for (int i = 0; i < keys.Count; i++) + for (int i = 0; i < keys.Count; i++) { PgpPublicKey key = (PgpPublicKey) keys[i]; - if (key.KeyId == pubKey.KeyId) + if (key.KeyId == pubKey.KeyId) { found = true; keys.RemoveAt(i); } } - return found ? new PgpPublicKeyRing(keys) : null; + return found ? new PgpPublicKeyRing(keys) : null; } - internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput) - { + internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput) + { PublicKeyPacket pk = (PublicKeyPacket) bcpgInput.ReadPacket(); - TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput); + TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput); - // PGP 8 actually leaves out the signature. - IList sigList = ReadSignaturesAndTrust(bcpgInput); + // PGP 8 actually leaves out the signature. + IList sigList = ReadSignaturesAndTrust(bcpgInput); - return new PgpPublicKey(pk, kTrust, sigList); - } + return new PgpPublicKey(pk, kTrust, sigList); + } } } diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 84d23614f..1027393ce 100644 --- a/crypto/src/openpgp/PgpSecretKey.cs +++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -2,8 +2,11 @@ using System; using System.Collections; using System.IO; +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.Security; using Org.BouncyCastle.Utilities; @@ -59,6 +62,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) privKey.Key; secKey = new DsaSecretBcpgKey(dsK.X); break; + case PublicKeyAlgorithmTag.ECDH: + case PublicKeyAlgorithmTag.ECDsa: + ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key; + secKey = new ECSecretBcpgKey(ecK.D); + break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key; @@ -362,24 +370,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp private byte[] ExtractKeyData( char[] passPhrase) { - SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm; + SymmetricKeyAlgorithmTag encAlgorithm = secret.EncAlgorithm; byte[] encData = secret.GetSecretKeyData(); - if (alg == SymmetricKeyAlgorithmTag.Null) + if (encAlgorithm == SymmetricKeyAlgorithmTag.Null) // TODO Check checksum here? return encData; - 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 'decryptData' try { @@ -389,9 +386,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp if (secret.PublicKeyPacket.Version >= 4) { - c.Init(false, new ParametersWithIV(key, iv)); - - data = c.DoFinal(encData); + data = RecoverKeyData(encAlgorithm, "/CFB/NoPadding", key, iv, encData, 0, encData.Length); bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1; byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2); @@ -417,15 +412,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp 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); + byte[] tmp = RecoverKeyData(encAlgorithm, "/CFB/NoPadding", key, iv, encData, pos, encLen); + Array.Copy(tmp, 0, data, pos, encLen); pos += encLen; if (i != 3) @@ -469,6 +463,25 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } + private static byte[] RecoverKeyData(SymmetricKeyAlgorithmTag encAlgorithm, string modeAndPadding, + KeyParameter key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + { + IBufferedCipher c; + try + { + string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm); + c = CipherUtilities.GetCipher(cName + modeAndPadding); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + + c.Init(false, new ParametersWithIV(key, iv)); + + return c.DoFinal(keyData, keyOff, keyLen); + } + /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary> public PgpPrivateKey ExtractPrivateKey( char[] passPhrase) @@ -506,6 +519,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp DsaParameters dsaParams = new DsaParameters(dsaPub.P, dsaPub.Q, dsaPub.G); privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams); break; + case PublicKeyAlgorithmTag.ECDH: + privateKey = GetECKey("ECDH", bcpgIn); + break; + case PublicKeyAlgorithmTag.ECDsa: + privateKey = GetECKey("ECDSA", bcpgIn); + break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key; @@ -517,7 +536,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp throw new PgpException("unknown public key algorithm encountered"); } - return new PgpPrivateKey(privateKey, KeyId); + return new PgpPrivateKey(KeyId, pubPk, privateKey); } catch (PgpException e) { @@ -529,6 +548,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } + private ECPrivateKeyParameters GetECKey(string algorithm, BcpgInputStream bcpgIn) + { + ECPublicBcpgKey ecdsaPub = (ECPublicBcpgKey)secret.PublicKeyPacket.Key; + ECSecretBcpgKey ecdsaPriv = new ECSecretBcpgKey(bcpgIn); + return new ECPrivateKeyParameters(algorithm, ecdsaPriv.X, ecdsaPub.CurveOid); + } + private static byte[] Checksum( bool useSha1, byte[] bytes, @@ -752,5 +778,174 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return c.DoFinal(rawKeyData); } + + /** + * Parse a secret key from one of the GPG S expression keys associating it with the passed in public key. + * + * @return a secret key object. + */ + public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase, PgpPublicKey pubKey) + { + SXprUtilities.SkipOpenParenthesis(inputStream); + + string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (type.Equals("protected-private-key")) + { + SXprUtilities.SkipOpenParenthesis(inputStream); + + string curveName; + + string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (keyType.Equals("ecc")) + { + SXprUtilities.SkipOpenParenthesis(inputStream); + + string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + + SXprUtilities.SkipCloseParenthesis(inputStream); + } + else + { + throw new PgpException("no curve details found"); + } + + byte[] qVal; + + SXprUtilities.SkipOpenParenthesis(inputStream); + + type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (type.Equals("q")) + { + qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + } + else + { + throw new PgpException("no q value found"); + } + + SXprUtilities.SkipCloseParenthesis(inputStream); + + byte[] dValue = GetDValue(inputStream, passPhrase, curveName); + // TODO: check SHA-1 hash. + + return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null, + new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), pubKey); + } + + throw new PgpException("unknown key type found"); + } + + /** + * Parse a secret key from one of the GPG S expression keys. + * + * @return a secret key object. + */ + public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase) + { + SXprUtilities.SkipOpenParenthesis(inputStream); + + string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (type.Equals("protected-private-key")) + { + SXprUtilities.SkipOpenParenthesis(inputStream); + + string curveName; + + string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (keyType.Equals("ecc")) + { + SXprUtilities.SkipOpenParenthesis(inputStream); + + string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + + if (curveName.StartsWith("NIST ")) + { + curveName = curveName.Substring("NIST ".Length); + } + + SXprUtilities.SkipCloseParenthesis(inputStream); + } + else + { + throw new PgpException("no curve details found"); + } + + byte[] qVal; + + SXprUtilities.SkipOpenParenthesis(inputStream); + + type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (type.Equals("q")) + { + qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + } + else + { + throw new PgpException("no q value found"); + } + + PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow, + new ECDsaPublicBcpgKey(ECNamedCurveTable.GetOid(curveName), new BigInteger(1, qVal))); + + SXprUtilities.SkipCloseParenthesis(inputStream); + + byte[] dValue = GetDValue(inputStream, passPhrase, curveName); + // TODO: check SHA-1 hash. + + return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null, + new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), new PgpPublicKey(pubPacket)); + } + + throw new PgpException("unknown key type found"); + } + + private static byte[] GetDValue(Stream inputStream, char[] passPhrase, string curveName) + { + string type; + SXprUtilities.SkipOpenParenthesis(inputStream); + + string protection; + S2k s2k; + byte[] iv; + byte[] secKeyData; + + type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + if (type.Equals("protected")) + { + protection = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + + SXprUtilities.SkipOpenParenthesis(inputStream); + + s2k = SXprUtilities.ParseS2k(inputStream); + + iv = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + + SXprUtilities.SkipCloseParenthesis(inputStream); + + secKeyData = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + } + else + { + throw new PgpException("protected block not found"); + } + + // TODO: recognise other algorithms + KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, passPhrase); + + byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length); + + // + // parse the secret key S-expr + // + Stream keyIn = new MemoryStream(data, false); + + SXprUtilities.SkipOpenParenthesis(keyIn); + SXprUtilities.SkipOpenParenthesis(keyIn); + SXprUtilities.SkipOpenParenthesis(keyIn); + String name = SXprUtilities.ReadString(keyIn, keyIn.ReadByte()); + return SXprUtilities.ReadBytes(keyIn, keyIn.ReadByte()); + } } } diff --git a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
index 4adf64012..d2177d09c 100644 --- a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs +++ b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
@@ -25,7 +25,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp list.Add(new Exportable(isCritical, isExportable)); } - /// <summary> + public void SetFeature( + bool isCritical, + byte feature) + { + list.Add(new Features(isCritical, feature)); + } + + /// <summary> /// 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. /// </summary> @@ -117,7 +124,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp list.Add(new SignerUserId(isCritical, userId)); } - public void SetEmbeddedSignature( + public void SetSignerUserId( + bool isCritical, + byte[] rawUserId) + { + if (rawUserId == null) + throw new ArgumentNullException("rawUserId"); + + list.Add(new SignerUserId(isCritical, false, rawUserId)); + } + + public void SetEmbeddedSignature( bool isCritical, PgpSignature pgpSignature) { @@ -136,7 +153,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length); - list.Add(new EmbeddedSignature(isCritical, data)); + list.Add(new EmbeddedSignature(isCritical, false, data)); } public void SetPrimaryUserId( diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
index 68fe4b594..156243f4e 100644 --- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs +++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -209,7 +209,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return list; } - [Obsolete("Use 'Count' property instead")] + public Features GetFeatures() + { + SignatureSubpacket p = this.GetSubpacket(SignatureSubpacketTag.Features); + + if (p == null) + return null; + + return new Features(p.IsCritical(), p.IsLongLength(), p.GetData()); + } + + [Obsolete("Use 'Count' property instead")] public int Size { get { return packets.Length; } diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
index 32e37b819..e4551db07 100644 --- a/crypto/src/openpgp/PgpUtilities.cs +++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -86,7 +86,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp case PublicKeyAlgorithmTag.Dsa: encAlg = "DSA"; break; - case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. + case PublicKeyAlgorithmTag.ECDH: + encAlg = "ECDH"; + break; + case PublicKeyAlgorithmTag.ECDsa: + encAlg = "ECDSA"; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. case PublicKeyAlgorithmTag.ElGamalGeneral: encAlg = "ElGamal"; break; @@ -135,7 +141,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) + public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) { int keySize; switch (algorithm) @@ -193,7 +199,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp char[] passPhrase) { int keySize = GetKeySize(algorithm); - byte[] pBytes = Strings.ToByteArray(new string(passPhrase)); + byte[] pBytes = Encoding.UTF8.GetBytes(passPhrase); byte[] keyBytes = new byte[(keySize + 7) / 8]; int generatedBytes = 0; @@ -431,5 +437,22 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return new ArmoredInputStream(inputStream, hasHeaders); } } + + internal static IWrapper CreateWrapper(SymmetricKeyAlgorithmTag encAlgorithm) + { + switch (encAlgorithm) + { + case SymmetricKeyAlgorithmTag.Aes128: + case SymmetricKeyAlgorithmTag.Aes192: + case SymmetricKeyAlgorithmTag.Aes256: + return WrapperUtilities.GetWrapper("AESWRAP"); + case SymmetricKeyAlgorithmTag.Camellia128: + case SymmetricKeyAlgorithmTag.Camellia192: + case SymmetricKeyAlgorithmTag.Camellia256: + return WrapperUtilities.GetWrapper("CAMELLIAWRAP"); + default: + throw new PgpException("unknown wrap algorithm: " + encAlgorithm); + } + } } } diff --git a/crypto/src/openpgp/Rfc6637Utilities.cs b/crypto/src/openpgp/Rfc6637Utilities.cs new file mode 100644
index 000000000..5d992ec51 --- /dev/null +++ b/crypto/src/openpgp/Rfc6637Utilities.cs
@@ -0,0 +1,138 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public sealed class Rfc6637Utilities + { + private Rfc6637Utilities() + { + } + + // "Anonymous Sender ", which is the octet sequence + private static readonly byte[] ANONYMOUS_SENDER = Hex.Decode("416E6F6E796D6F75732053656E64657220202020"); + + public static string GetAgreementAlgorithm(PublicKeyPacket pubKeyData) + { + ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key; + + switch (ecKey.HashAlgorithm) + { + case HashAlgorithmTag.Sha256: + return "ECCDHwithSHA256CKDF"; + case HashAlgorithmTag.Sha384: + return "ECCDHwithSHA384CKDF"; + case HashAlgorithmTag.Sha512: + return "ECCDHwithSHA512CKDF"; + default: + throw new ArgumentException("Unknown hash algorithm specified: " + ecKey.HashAlgorithm); + } + } + + public static DerObjectIdentifier GetKeyEncryptionOID(SymmetricKeyAlgorithmTag algID) + { + switch (algID) + { + case SymmetricKeyAlgorithmTag.Aes128: + return NistObjectIdentifiers.IdAes128Wrap; + case SymmetricKeyAlgorithmTag.Aes192: + return NistObjectIdentifiers.IdAes192Wrap; + case SymmetricKeyAlgorithmTag.Aes256: + return NistObjectIdentifiers.IdAes256Wrap; + default: + throw new PgpException("unknown symmetric algorithm ID: " + algID); + } + } + + public static int GetKeyLength(SymmetricKeyAlgorithmTag algID) + { + switch (algID) + { + case SymmetricKeyAlgorithmTag.Aes128: + return 16; + case SymmetricKeyAlgorithmTag.Aes192: + return 24; + case SymmetricKeyAlgorithmTag.Aes256: + return 32; + default: + throw new PgpException("unknown symmetric algorithm ID: " + algID); + } + } + + public static byte[] CreateKey(PublicKeyPacket pubKeyData, ECPoint s) + { + byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData); + + ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key; + + return Kdf(ecKey.HashAlgorithm, s, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial); + } + + // RFC 6637 - Section 8 + // curve_OID_len = (byte)len(curve_OID); + // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 + // || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous + // Sender " || recipient_fingerprint; + // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap + // Compute Z = KDF( S, Z_len, Param ); + public static byte[] CreateUserKeyingMaterial(PublicKeyPacket pubKeyData) + { + MemoryStream pOut = new MemoryStream(); + ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key; + byte[] encOid = ecKey.CurveOid.GetEncoded(); + + pOut.Write(encOid, 1, encOid.Length - 1); + pOut.WriteByte((byte)pubKeyData.Algorithm); + pOut.WriteByte(0x03); + pOut.WriteByte(0x01); + pOut.WriteByte((byte)ecKey.HashAlgorithm); + pOut.WriteByte((byte)ecKey.SymmetricKeyAlgorithm); + pOut.Write(ANONYMOUS_SENDER, 0, ANONYMOUS_SENDER.Length); + + byte[] fingerprint = PgpPublicKey.CalculateFingerprint(pubKeyData); + pOut.Write(fingerprint, 0, fingerprint.Length); + + return pOut.ToArray(); + } + + // RFC 6637 - Section 7 + // Implements KDF( X, oBits, Param ); + // Input: point X = (x,y) + // oBits - the desired size of output + // hBits - the size of output of hash function Hash + // Param - octets representing the parameters + // Assumes that oBits <= hBits + // Convert the point X to the octet string, see section 6: + // ZB' = 04 || x || y + // and extract the x portion from ZB' + // ZB = x; + // MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param ); + // return oBits leftmost bits of MB. + private static byte[] Kdf(HashAlgorithmTag digestAlg, ECPoint s, int keyLen, byte[] parameters) + { + byte[] ZB = s.XCoord.GetEncoded(); + + string digestName = PgpUtilities.GetDigestName(digestAlg); + IDigest digest = DigestUtilities.GetDigest(digestName); + + digest.Update(0x00); + digest.Update(0x00); + digest.Update(0x00); + digest.Update(0x01); + digest.BlockUpdate(ZB, 0, ZB.Length); + digest.BlockUpdate(parameters, 0, parameters.Length); + + byte[] hash = DigestUtilities.DoFinal(digest); + + return Arrays.CopyOfRange(hash, 0, keyLen); + } + } +} diff --git a/crypto/src/openpgp/SXprUtilities.cs b/crypto/src/openpgp/SXprUtilities.cs new file mode 100644
index 000000000..68ff373a8 --- /dev/null +++ b/crypto/src/openpgp/SXprUtilities.cs
@@ -0,0 +1,102 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /** + * Utility functions for looking a S-expression keys. This class will move when it finds a better home! + * <p> + * Format documented here: + * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master + * </p> + */ + public sealed class SXprUtilities + { + private SXprUtilities() + { + } + + private static int ReadLength(Stream input, int ch) + { + int len = ch - '0'; + + while ((ch = input.ReadByte()) >= 0 && ch != ':') + { + len = len * 10 + ch - '0'; + } + + return len; + } + + internal static string ReadString(Stream input, int ch) + { + int len = ReadLength(input, ch); + + char[] chars = new char[len]; + + for (int i = 0; i != chars.Length; i++) + { + chars[i] = (char)input.ReadByte(); + } + + return new string(chars); + } + + internal static byte[] ReadBytes(Stream input, int ch) + { + int len = ReadLength(input, ch); + + byte[] data = new byte[len]; + + Streams.ReadFully(input, data); + + return data; + } + + internal static S2k ParseS2k(Stream input) + { + SkipOpenParenthesis(input); + + string alg = ReadString(input, input.ReadByte()); + byte[] iv = ReadBytes(input, input.ReadByte()); + long iterationCount = Int64.Parse(ReadString(input, input.ReadByte())); + + SkipCloseParenthesis(input); + + // we have to return the actual iteration count provided. + return new MyS2k(HashAlgorithmTag.Sha1, iv, iterationCount); + } + + internal static void SkipOpenParenthesis(Stream input) + { + int ch = input.ReadByte(); + if (ch != '(') + throw new IOException("unknown character encountered"); + } + + internal static void SkipCloseParenthesis(Stream input) + { + int ch = input.ReadByte(); + if (ch != ')') + throw new IOException("unknown character encountered"); + } + + private class MyS2k : S2k + { + private readonly long mIterationCount64; + + internal MyS2k(HashAlgorithmTag algorithm, byte[] iv, long iterationCount64) + : base(algorithm, iv, (int)iterationCount64) + { + this.mIterationCount64 = iterationCount64; + } + + public override long IterationCount + { + get { return mIterationCount64; } + } + } + } +} diff --git a/crypto/src/openpgp/WrappedGeneratorStream.cs b/crypto/src/openpgp/WrappedGeneratorStream.cs
index 6fc7329af..cdc9befb3 100644 --- a/crypto/src/openpgp/WrappedGeneratorStream.cs +++ b/crypto/src/openpgp/WrappedGeneratorStream.cs
@@ -1,6 +1,6 @@ using System.IO; -using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Bcpg.OpenPgp { diff --git a/crypto/src/openssl/MiscPemGenerator.cs b/crypto/src/openssl/MiscPemGenerator.cs
index c4c537904..6b91e8b1c 100644 --- a/crypto/src/openssl/MiscPemGenerator.cs +++ b/crypto/src/openssl/MiscPemGenerator.cs
@@ -21,117 +21,117 @@ using Org.BouncyCastle.X509; namespace Org.BouncyCastle.OpenSsl { - /** - * PEM generator for the original set of PEM objects used in Open SSL. - */ - public class MiscPemGenerator - : PemObjectGenerator - { - private object obj; - private string algorithm; - private char[] password; - private SecureRandom random; - - public MiscPemGenerator(object obj) - { - this.obj = obj; - } - - public MiscPemGenerator( - object obj, - string algorithm, - char[] password, - SecureRandom random) - { - this.obj = obj; - this.algorithm = algorithm; - this.password = password; - this.random = random; - } - - private static PemObject CreatePemObject(object obj) - { - if (obj == null) - throw new ArgumentNullException("obj"); - - if (obj is AsymmetricCipherKeyPair) - { - return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private); - } - - string type; - byte[] encoding; - - if (obj is PemObject) - return (PemObject)obj; - - if (obj is PemObjectGenerator) - return ((PemObjectGenerator)obj).Generate(); - - 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 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 PemGenerationException("Object type not supported: " + obj.GetType().FullName); - } - - return new PemObject(type, encoding); - } + /** + * PEM generator for the original set of PEM objects used in Open SSL. + */ + public class MiscPemGenerator + : PemObjectGenerator + { + private object obj; + private string algorithm; + private char[] password; + private SecureRandom random; + + public MiscPemGenerator(object obj) + { + this.obj = obj; + } + + public MiscPemGenerator( + object obj, + string algorithm, + char[] password, + SecureRandom random) + { + this.obj = obj; + this.algorithm = algorithm; + this.password = password; + this.random = random; + } + + private static PemObject CreatePemObject(object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + if (obj is AsymmetricCipherKeyPair) + { + return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private); + } + + string type; + byte[] encoding; + + if (obj is PemObject) + return (PemObject)obj; + + if (obj is PemObjectGenerator) + return ((PemObjectGenerator)obj).Generate(); + + 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 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 PemGenerationException("Object type not supported: " + obj.GetType().FullName); + } + + return new PemObject(type, encoding); + } // private string GetHexEncoded(byte[] bytes) // { @@ -147,130 +147,130 @@ namespace Org.BouncyCastle.OpenSsl // return new string(chars); // } - private static PemObject CreatePemObject( - 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) - { - return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private, algorithm, password, random); - } - - 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 PemGenerationException("Object type not supported: " + obj.GetType().FullName); - } - - - string dekAlgName = Platform.ToUpperInvariant(algorithm); + private static PemObject CreatePemObject( + 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) + { + return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private, algorithm, password, random); + } + + 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 PemGenerationException("Object type not supported: " + obj.GetType().FullName); + } + + + string dekAlgName = Platform.ToUpperInvariant(algorithm); // 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); - - IList headers = Platform.CreateArrayList(2); - - headers.Add(new PemHeader("Proc-Type", "4,ENCRYPTED")); - headers.Add(new PemHeader("DEK-Info", dekAlgName + "," + Hex.ToHexString(iv))); - - return new PemObject(type, headers, encData); - } - - private static 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"; - } - else if (oid.Equals(CryptoProObjectIdentifiers.GostR3410x2001) - || oid.Equals(X9ObjectIdentifiers.IdECPublicKey)) - { - keyType = "EC"; - } - else - { - throw new ArgumentException("Cannot handle private key of type: " + akp.GetType().FullName, "akp"); - } - - return info.PrivateKey.GetEncoded(); - } - - public PemObject Generate() - { - try - { - if (algorithm != null) - { - return CreatePemObject(obj, algorithm, password, random); - } - - return CreatePemObject(obj); - } - catch (IOException e) - { - throw new PemGenerationException("encoding exception", e); - } - } - } + 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); + + IList headers = Platform.CreateArrayList(2); + + headers.Add(new PemHeader("Proc-Type", "4,ENCRYPTED")); + headers.Add(new PemHeader("DEK-Info", dekAlgName + "," + Hex.ToHexString(iv))); + + return new PemObject(type, headers, encData); + } + + private static byte[] EncodePrivateKey( + AsymmetricKeyParameter akp, + out string keyType) + { + PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp); + AlgorithmIdentifier algID = info.PrivateKeyAlgorithm; + DerObjectIdentifier oid = algID.ObjectID; + + if (oid.Equals(X9ObjectIdentifiers.IdDsa)) + { + keyType = "DSA"; + + DsaParameter p = DsaParameter.GetInstance(algID.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"; + } + else if (oid.Equals(CryptoProObjectIdentifiers.GostR3410x2001) + || oid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + keyType = "EC"; + } + else + { + throw new ArgumentException("Cannot handle private key of type: " + akp.GetType().FullName, "akp"); + } + + return info.ParsePrivateKey().GetEncoded(); + } + + public PemObject Generate() + { + try + { + if (algorithm != null) + { + return CreatePemObject(obj, algorithm, password, random); + } + + return CreatePemObject(obj); + } + catch (IOException e) + { + throw new PemGenerationException("encoding exception", e); + } + } + } } diff --git a/crypto/src/openssl/PEMReader.cs b/crypto/src/openssl/PEMReader.cs
index 9d3560838..ec5d1b414 100644 --- a/crypto/src/openssl/PEMReader.cs +++ b/crypto/src/openssl/PEMReader.cs
@@ -109,6 +109,7 @@ namespace Org.BouncyCastle.OpenSsl case "X509 CERTIFICATE": return ReadCertificate(obj); case "PKCS7": + case "CMS": return ReadPkcs7(obj); case "X509 CRL": return ReadCrl(obj); @@ -276,7 +277,7 @@ namespace Org.BouncyCastle.OpenSsl if (seq.Count != 9) throw new PemException("malformed sequence in RSA private key"); - RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq); + RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(seq); pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent); privSpec = new RsaPrivateCrtKeyParameters( diff --git a/crypto/src/pkcs/Pkcs10CertificationRequest.cs b/crypto/src/pkcs/Pkcs10CertificationRequest.cs
index 9f24eb18a..b68979cad 100644 --- a/crypto/src/pkcs/Pkcs10CertificationRequest.cs +++ b/crypto/src/pkcs/Pkcs10CertificationRequest.cs
@@ -15,6 +15,7 @@ using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; using Org.BouncyCastle.X509; +using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.Pkcs { @@ -198,17 +199,18 @@ namespace Org.BouncyCastle.Pkcs Stream input) : base((Asn1Sequence) Asn1Object.FromStream(input)) { - } - - /// <summary> - /// Instantiate a Pkcs10CertificationRequest object with the necessary credentials. - /// </summary> - ///<param name="signatureAlgorithm">Name of Sig Alg.</param> - /// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param> - /// <param name="publicKey">Public Key to be included in cert reqest.</param> - /// <param name="attributes">ASN1Set of Attributes.</param> - /// <param name="signingKey">Matching Private key for nominated (above) public key to be used to sign the request.</param> - public Pkcs10CertificationRequest( + } + + /// <summary> + /// Instantiate a Pkcs10CertificationRequest object with the necessary credentials. + /// </summary> + ///<param name="signatureAlgorithm">Name of Sig Alg.</param> + /// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param> + /// <param name="publicKey">Public Key to be included in cert reqest.</param> + /// <param name="attributes">ASN1Set of Attributes.</param> + /// <param name="signingKey">Matching Private key for nominated (above) public key to be used to sign the request.</param> + [Obsolete("Use constructor with an ISignatureCalculator")] + public Pkcs10CertificationRequest( string signatureAlgorithm, X509Name subject, AsymmetricKeyParameter publicKey, @@ -226,79 +228,84 @@ namespace Org.BouncyCastle.Pkcs if (!signingKey.IsPrivate) throw new ArgumentException("key for signing must be private", "signingKey"); -// DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm); - string algorithmName = Platform.ToUpperInvariant(signatureAlgorithm); - DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName]; - - if (sigOid == null) - { - try - { - sigOid = new DerObjectIdentifier(algorithmName); - } - catch (Exception e) - { - throw new ArgumentException("Unknown signature type requested", e); - } - } - - if (noParams.Contains(sigOid)) - { - this.sigAlgId = new AlgorithmIdentifier(sigOid); - } - else if (exParams.Contains(algorithmName)) - { - this.sigAlgId = new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); - } - else - { - this.sigAlgId = new AlgorithmIdentifier(sigOid, DerNull.Instance); - } - - 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()); + init(new Asn1SignatureCalculator(signatureAlgorithm, signingKey), subject, publicKey, attributes, signingKey); } -// 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); -// } -// } - - /// <summary> - /// Get the public key. - /// </summary> - /// <returns>The public key.</returns> - public AsymmetricKeyParameter GetPublicKey() + /// <summary> + /// Instantiate a Pkcs10CertificationRequest object with the necessary credentials. + /// </summary> + ///<param name="signatureCalculator">The signature calculator to sign the PKCS#10 request with.</param> + /// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param> + /// <param name="publicKey">Public Key to be included in cert reqest.</param> + /// <param name="attributes">ASN1Set of Attributes.</param> + /// <param name="signingKey">Matching Private key for nominated (above) public key to be used to sign the request.</param> + public Pkcs10CertificationRequest( + ISignatureCalculator signatureCalculator, + X509Name subject, + AsymmetricKeyParameter publicKey, + Asn1Set attributes, + AsymmetricKeyParameter signingKey) + { + if (signatureCalculator == null) + throw new ArgumentNullException("signatureCalculator"); + 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"); + + init(signatureCalculator, subject, publicKey, attributes, signingKey); + } + + private void init( + ISignatureCalculator signatureCalculator, + X509Name subject, + AsymmetricKeyParameter publicKey, + Asn1Set attributes, + AsymmetricKeyParameter signingKey) + { + this.sigAlgId = (AlgorithmIdentifier)signatureCalculator.AlgorithmDetails; + + SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + + this.reqInfo = new CertificationRequestInfo(subject, pubInfo, attributes); + + IStreamCalculator streamCalculator = signatureCalculator.CreateCalculator(); + + byte[] reqInfoData = reqInfo.GetDerEncoded(); + + streamCalculator.Stream.Write(reqInfoData, 0, reqInfoData.Length); + + streamCalculator.Stream.Close(); + + // Generate Signature. + sigBits = new DerBitString(((IBlockResult)streamCalculator.GetResult()).DoFinal()); + } + + // 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); + // } + // } + + /// <summary> + /// Get the public key. + /// </summary> + /// <returns>The public key.</returns> + public AsymmetricKeyParameter GetPublicKey() { return PublicKeyFactory.CreateKey(reqInfo.SubjectPublicKeyInfo); } @@ -315,55 +322,47 @@ namespace Org.BouncyCastle.Pkcs 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()); + return Verify(new Asn1SignatureVerifierProvider(publicKey)); } -// /// <summary> -// /// Get the Der Encoded Pkcs10 Certification Request. -// /// </summary> -// /// <returns>A byte array.</returns> -// public byte[] GetEncoded() -// { -// return new CertificationRequest(reqInfo, sigAlgId, sigBits).GetDerEncoded(); -// } - - // TODO Figure out how to set parameters on an ISigner - private void SetSignatureParameters( + public bool Verify( + ISignatureVerifierProvider verifierProvider) + { + return Verify(verifierProvider.CreateSignatureVerifier(sigAlgId)); + } + + public bool Verify( + ISignatureVerifier verifier) + { + try + { + byte[] b = reqInfo.GetDerEncoded(); + + IStreamCalculator streamCalculator = verifier.CreateCalculator(); + + streamCalculator.Stream.Write(b, 0, b.Length); + + streamCalculator.Stream.Close(); + + return ((IVerifier)streamCalculator.GetResult()).IsVerified(sigBits.GetBytes()); + } + catch (Exception e) + { + throw new SignatureException("exception encoding TBS cert request", e); + } + } + + // /// <summary> + // /// Get the Der Encoded Pkcs10 Certification Request. + // /// </summary> + // /// <returns>A byte array.</returns> + // 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) { diff --git a/crypto/src/pkcs/Pkcs12Store.cs b/crypto/src/pkcs/Pkcs12Store.cs
index 40364eec7..e4fe29401 100644 --- a/crypto/src/pkcs/Pkcs12Store.cs +++ b/crypto/src/pkcs/Pkcs12Store.cs
@@ -28,6 +28,8 @@ namespace Org.BouncyCastle.Pkcs private readonly DerObjectIdentifier certAlgorithm; private readonly bool useDerEncoding; + private AsymmetricKeyEntry unmarkedKeyEntry = null; + private const int MinIterations = 1024; private const int SaltSize = 20; @@ -107,22 +109,101 @@ namespace Org.BouncyCastle.Pkcs Load(input, password); } + protected virtual void LoadKeyBag(PrivateKeyInfo privKeyInfo, Asn1Set bagAttributes) + { + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo); + + IDictionary attributes = Platform.CreateHashtable(); + AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privKey, attributes); + + string alias = null; + Asn1OctetString localId = null; + + if (bagAttributes != null) + { + foreach (Asn1Sequence sq in bagAttributes) + { + DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]); + Asn1Set attrSet = Asn1Set.GetInstance(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, differing values give an error + if (attributes.Contains(aOid.Id)) + { + // OK, but the value has to be the same + if (!attributes[aOid.Id].Equals(attr)) + throw new IOException("attempt to add existing attribute with different value"); + } + else + { + 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] = keyEntry; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + } + + if (localId != null) + { + string name = Hex.ToHexString(localId.GetOctets()); + + if (alias == null) + { + keys[name] = keyEntry; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else + { + unmarkedKeyEntry = keyEntry; + } + } + + protected virtual void LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo encPrivKeyInfo, Asn1Set bagAttributes, + char[] password, bool wrongPkcs12Zero) + { + if (password != null) + { + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo( + password, wrongPkcs12Zero, encPrivKeyInfo); + + LoadKeyBag(privInfo, bagAttributes); + } + } + public void Load( 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 + if (password != null && bag.MacData != null) // check the mac code { MacData mData = bag.MacData; DigestInfo dInfo = mData.Mac; @@ -152,8 +233,9 @@ namespace Org.BouncyCastle.Pkcs keys.Clear(); localIds.Clear(); + unmarkedKeyEntry = null; - IList chain = Platform.CreateArrayList(); + IList certBags = Platform.CreateArrayList(); if (info.ContentType.Equals(PkcsObjectIdentifiers.Data)) { @@ -166,109 +248,28 @@ namespace Org.BouncyCastle.Pkcs { DerObjectIdentifier oid = ci.ContentType; + byte[] octets = null; if (oid.Equals(PkcsObjectIdentifiers.Data)) { - byte[] octets = ((Asn1OctetString)ci.Content).GetOctets(); - Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); - - foreach (Asn1Sequence subSeq in seq) + octets = ((Asn1OctetString)ci.Content).GetOctets(); + } + else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) + { + if (password != null) { - 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 - // - IDictionary attributes = Platform.CreateHashtable(); - 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, differing values give an error - if (attributes.Contains(aOid.Id)) - { - // OK, but the value has to be the same - if (!attributes[aOid.Id].Equals(attr)) - { - throw new IOException("attempt to add existing attribute with different value"); - } - } - else - { - 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) - { - string name = Hex.ToHexString(localId.GetOctets()); - - 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)); - } + EncryptedData d = EncryptedData.GetInstance(ci.Content); + octets = CryptPbeData(false, d.EncryptionAlgorithm, + password, wrongPkcs12Zero, d.Content.GetOctets()); } } - else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) + else + { + // TODO Other data types + } + + if (octets != null) { - EncryptedData d = EncryptedData.GetInstance(ci.Content); - byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm, - password, wrongPkcs12Zero, d.Content.GetOctets()); - Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); + Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(octets); foreach (Asn1Sequence subSeq in seq) { @@ -276,156 +277,23 @@ namespace Org.BouncyCastle.Pkcs if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) { - chain.Add(b); + certBags.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 - // - IDictionary attributes = Platform.CreateHashtable(); - 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, differing values give an error - if (attributes.Contains(aOid.Id)) - { - // OK, but the value has to be the same - if (!attributes[aOid.Id].Equals(attr)) - { - throw new IOException("attempt to add existing attribute with different value"); - } - } - else - { - 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? - - string name = Hex.ToHexString(localId.GetOctets()); - - if (alias == null) - { - keys[name] = pkcs12Key; - } - else - { - // TODO There may have been more than one alias - localIds[alias] = name; - } + LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo.GetInstance(b.BagValue), + b.BagAttributes, password, wrongPkcs12Zero); } 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; - IDictionary attributes = Platform.CreateHashtable(); - AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); - - foreach (Asn1Sequence sq in b.BagAttributes) - { - DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]); - Asn1Set attrSet = Asn1Set.GetInstance(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, differing values give an error - if (attributes.Contains(aOid.Id)) - { - // OK, but the value has to be the same - if (!attributes[aOid.Id].Equals(attr)) - { - throw new IOException("attempt to add existing attribute with different value"); - } - } - else - { - 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? - - string name = Hex.ToHexString(localId.GetOctets()); - - if (alias == null) - { - keys[name] = pkcs12Key; - } - else - { - // TODO There may have been more than one alias - localIds[alias] = name; - } + LoadKeyBag(PrivateKeyInfo.GetInstance(b.BagValue), b.BagAttributes); } else { - Console.WriteLine("extra " + b.BagID); - Console.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + // TODO Other bag types } } } - else - { - Console.WriteLine("extra " + oid); - Console.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content)); - } } } @@ -433,10 +301,10 @@ namespace Org.BouncyCastle.Pkcs chainCerts.Clear(); keyCerts.Clear(); - foreach (SafeBag b in chain) + foreach (SafeBag b in certBags) { - CertBag cb = new CertBag((Asn1Sequence)b.BagValue); - byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets(); + CertBag certBag = new CertBag((Asn1Sequence)b.BagValue); + byte[] octets = ((Asn1OctetString)certBag.CertValue).GetOctets(); X509Certificate cert = new X509CertificateParser().ReadCertificate(octets); // @@ -486,21 +354,18 @@ namespace Org.BouncyCastle.Pkcs } CertId certId = new CertId(cert.GetPublicKey()); - X509CertificateEntry pkcs12Cert = new X509CertificateEntry(cert, attributes); + X509CertificateEntry certEntry = new X509CertificateEntry(cert, attributes); - chainCerts[certId] = pkcs12Cert; + chainCerts[certId] = certEntry; - if (unmarkedKey) + if (unmarkedKeyEntry != null) { if (keyCerts.Count == 0) { string name = Hex.ToHexString(certId.Id); - keyCerts[name] = pkcs12Cert; - - object temp = keys["unmarked"]; - keys.Remove("unmarked"); - keys[name] = temp; + keyCerts[name] = certEntry; + keys[name] = unmarkedKeyEntry; } } else @@ -509,13 +374,13 @@ namespace Org.BouncyCastle.Pkcs { string name = Hex.ToHexString(localId.GetOctets()); - keyCerts[name] = pkcs12Cert; + keyCerts[name] = certEntry; } if (alias != null) { // TODO There may have been more than one alias - certs[alias] = pkcs12Cert; + certs[alias] = certEntry; } } } @@ -841,24 +706,34 @@ namespace Org.BouncyCastle.Pkcs { if (stream == null) throw new ArgumentNullException("stream"); - if (password == null) - throw new ArgumentNullException("password"); if (random == null) throw new ArgumentNullException("random"); // - // handle the key + // handle the keys // - Asn1EncodableVector keyS = new Asn1EncodableVector(); + Asn1EncodableVector keyBags = 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); + AsymmetricKeyEntry privKey = (AsymmetricKeyEntry)keys[name]; + + DerObjectIdentifier bagOid; + Asn1Encodable bagData; + + if (password == null) + { + bagOid = PkcsObjectIdentifiers.KeyBag; + bagData = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey.Key); + } + else + { + bagOid = PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag; + bagData = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + keyAlgorithm, password, kSalt, MinIterations, privKey.Key); + } Asn1EncodableVector kName = new Asn1EncodableVector(); @@ -903,13 +778,11 @@ namespace Org.BouncyCastle.Pkcs new DerSet(subjectKeyID))); } - SafeBag kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName)); - keyS.Add(kBag); + keyBags.Add(new SafeBag(bagOid, bagData.ToAsn1Object(), new DerSet(kName))); } - byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded(); - - BerOctetString keyString = new BerOctetString(derEncodedBytes); + byte[] keyBagsEncoding = new DerSequence(keyBags).GetDerEncoded(); + ContentInfo keysInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(keyBagsEncoding)); // // certificate processing @@ -918,7 +791,7 @@ namespace Org.BouncyCastle.Pkcs random.NextBytes(cSalt); - Asn1EncodableVector certSeq = new Asn1EncodableVector(); + Asn1EncodableVector certBags = new Asn1EncodableVector(); Pkcs12PbeParams cParams = new Pkcs12PbeParams(cSalt, MinIterations); AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object()); ISet doneCerts = new HashSet(); @@ -972,10 +845,7 @@ namespace Org.BouncyCastle.Pkcs new DerSet(subjectKeyID))); } - SafeBag sBag = new SafeBag( - PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); - - certSeq.Add(sBag); + certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName))); doneCerts.Add(certEntry.Certificate); } @@ -1026,10 +896,7 @@ namespace Org.BouncyCastle.Pkcs new DerSet(new DerBmpString(certId)))); } - SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, - cBag.ToAsn1Object(), new DerSet(fName)); - - certSeq.Add(sBag); + certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName))); doneCerts.Add(cert.Certificate); } @@ -1062,22 +929,24 @@ namespace Org.BouncyCastle.Pkcs new DerSet(cert[oid]))); } - SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); - - certSeq.Add(sBag); + certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName))); } - derEncodedBytes = new DerSequence(certSeq).GetDerEncoded(); - - byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes); + byte[] certBagsEncoding = new DerSequence(certBags).GetDerEncoded(); - EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes)); - - ContentInfo[] info = new ContentInfo[] + ContentInfo certsInfo; + if (password == null) { - new ContentInfo(PkcsObjectIdentifiers.Data, keyString), - new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object()) - }; + certsInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(certBagsEncoding)); + } + else + { + byte[] certBytes = CryptPbeData(true, cAlgId, password, false, certBagsEncoding); + EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes)); + certsInfo = new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object()); + } + + ContentInfo[] info = new ContentInfo[]{ keysInfo, certsInfo }; byte[] data = new AuthenticatedSafe(info).GetEncoded( useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber); @@ -1087,22 +956,26 @@ namespace Org.BouncyCastle.Pkcs // // create the mac // - byte[] mSalt = new byte[20]; - random.NextBytes(mSalt); + MacData macData = null; + if (password != null) + { + byte[] mSalt = new byte[20]; + random.NextBytes(mSalt); - byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1, - mSalt, MinIterations, password, false, data); + byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1, + mSalt, MinIterations, password, false, data); - AlgorithmIdentifier algId = new AlgorithmIdentifier( - OiwObjectIdentifiers.IdSha1, DerNull.Instance); - DigestInfo dInfo = new DigestInfo(algId, mac); + AlgorithmIdentifier algId = new AlgorithmIdentifier( + OiwObjectIdentifiers.IdSha1, DerNull.Instance); + DigestInfo dInfo = new DigestInfo(algId, mac); - MacData mData = new MacData(dInfo, mSalt, MinIterations); + macData = new MacData(dInfo, mSalt, MinIterations); + } // // output the Pfx // - Pfx pfx = new Pfx(mainInfo, mData); + Pfx pfx = new Pfx(mainInfo, macData); DerOutputStream derOut; if (useDerEncoding) diff --git a/crypto/src/pkix/PkixCertPathChecker.cs b/crypto/src/pkix/PkixCertPathChecker.cs
index f22738d89..da7e82b46 100644 --- a/crypto/src/pkix/PkixCertPathChecker.cs +++ b/crypto/src/pkix/PkixCertPathChecker.cs
@@ -1,5 +1,3 @@ -using System; -using System.Collections; using Org.BouncyCastle.Utilities.Collections; using Org.BouncyCastle.X509; @@ -82,7 +80,7 @@ namespace Org.BouncyCastle.Pkix * @exception CertPathValidatorException * if the specified certificate does not pass the check */ - public abstract void Check(X509Certificate cert, ICollection unresolvedCritExts); + public abstract void Check(X509Certificate cert, ISet unresolvedCritExts); //throws CertPathValidatorException; /** diff --git a/crypto/src/pkix/PkixParameters.cs b/crypto/src/pkix/PkixParameters.cs
index 6df1b646f..47d3b5e37 100644 --- a/crypto/src/pkix/PkixParameters.cs +++ b/crypto/src/pkix/PkixParameters.cs
@@ -745,7 +745,7 @@ namespace Org.BouncyCastle.Pkix } /** - * Returns the neccessary attributes which must be contained in an attribute + * Returns the necessary attributes which must be contained in an attribute * certificate. * <p> * The returned <code>ISet</code> is immutable and contains @@ -760,7 +760,7 @@ namespace Org.BouncyCastle.Pkix } /** - * Sets the neccessary which must be contained in an attribute certificate. + * Sets the necessary which must be contained in an attribute certificate. * <p> * The <code>ISet</code> must contain <code>String</code>s with the * OIDs. diff --git a/crypto/src/security/AgreementUtilities.cs b/crypto/src/security/AgreementUtilities.cs
index 4c61ac354..12d427c8c 100644 --- a/crypto/src/security/AgreementUtilities.cs +++ b/crypto/src/security/AgreementUtilities.cs
@@ -22,14 +22,14 @@ namespace Org.BouncyCastle.Security private static readonly IDictionary algorithms = Platform.CreateHashtable(); //private static readonly IDictionary oids = Platform.CreateHashtable(); - static AgreementUtilities() + static AgreementUtilities() { - //algorithms[X9ObjectIdentifiers.DHSinglePassCofactorDHSha1KdfScheme.Id] = ?; + algorithms[X9ObjectIdentifiers.DHSinglePassCofactorDHSha1KdfScheme.Id] = "ECCDHWITHSHA1KDF"; algorithms[X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id] = "ECDHWITHSHA1KDF"; algorithms[X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id] = "ECMQVWITHSHA1KDF"; } - public static IBasicAgreement GetBasicAgreement( + public static IBasicAgreement GetBasicAgreement( DerObjectIdentifier oid) { return GetBasicAgreement(oid.Id); @@ -52,8 +52,8 @@ namespace Org.BouncyCastle.Security if (mechanism == "ECDH") return new ECDHBasicAgreement(); - if (mechanism == "ECDHC") - return new ECDHCBasicAgreement(); + if (mechanism == "ECDHC" || mechanism == "ECCDH") + return new ECDHCBasicAgreement(); if (mechanism == "ECMQV") return new ECMqvBasicAgreement(); diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs
index ec3f63940..7ddf6c8e4 100644 --- a/crypto/src/security/DigestUtilities.cs +++ b/crypto/src/security/DigestUtilities.cs
@@ -21,11 +21,13 @@ namespace Org.BouncyCastle.Security { private enum DigestAlgorithm { GOST3411, + KECCAK_224, KECCAK_256, KECCAK_288, KECCAK_384, KECCAK_512, MD2, MD4, MD5, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, SHA_1, SHA_224, SHA_256, SHA_384, SHA_512, SHA_512_224, SHA_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512, + SHAKE128, SHAKE256, TIGER, WHIRLPOOL, }; @@ -72,7 +74,12 @@ namespace Org.BouncyCastle.Security algorithms[CryptoProObjectIdentifiers.GostR3411.Id] = "GOST3411"; - + algorithms[NistObjectIdentifiers.IdSha3_224.Id] = "SHA3-224"; + algorithms[NistObjectIdentifiers.IdSha3_256.Id] = "SHA3-256"; + algorithms[NistObjectIdentifiers.IdSha3_384.Id] = "SHA3-384"; + algorithms[NistObjectIdentifiers.IdSha3_512.Id] = "SHA3-512"; + algorithms[NistObjectIdentifiers.IdShake128.Id] = "SHAKE128"; + algorithms[NistObjectIdentifiers.IdShake256.Id] = "SHAKE256"; oids["MD2"] = PkcsObjectIdentifiers.MD2; oids["MD4"] = PkcsObjectIdentifiers.MD4; @@ -84,6 +91,12 @@ namespace Org.BouncyCastle.Security oids["SHA-512"] = NistObjectIdentifiers.IdSha512; oids["SHA-512/224"] = NistObjectIdentifiers.IdSha512_224; oids["SHA-512/256"] = NistObjectIdentifiers.IdSha512_256; + oids["SHA3-224"] = NistObjectIdentifiers.IdSha3_224; + oids["SHA3-256"] = NistObjectIdentifiers.IdSha3_256; + oids["SHA3-384"] = NistObjectIdentifiers.IdSha3_384; + oids["SHA3-512"] = NistObjectIdentifiers.IdSha3_512; + oids["SHAKE128"] = NistObjectIdentifiers.IdShake128; + oids["SHAKE256"] = NistObjectIdentifiers.IdShake256; oids["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; oids["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; oids["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; @@ -141,7 +154,12 @@ namespace Org.BouncyCastle.Security switch (digestAlgorithm) { case DigestAlgorithm.GOST3411: return new Gost3411Digest(); - case DigestAlgorithm.MD2: return new MD2Digest(); + case DigestAlgorithm.KECCAK_224: return new KeccakDigest(224); + case DigestAlgorithm.KECCAK_256: return new KeccakDigest(256); + case DigestAlgorithm.KECCAK_288: return new KeccakDigest(288); + case DigestAlgorithm.KECCAK_384: return new KeccakDigest(384); + case DigestAlgorithm.KECCAK_512: return new KeccakDigest(512); + case DigestAlgorithm.MD2: return new MD2Digest(); case DigestAlgorithm.MD4: return new MD4Digest(); case DigestAlgorithm.MD5: return new MD5Digest(); case DigestAlgorithm.RIPEMD128: return new RipeMD128Digest(); @@ -159,6 +177,8 @@ namespace Org.BouncyCastle.Security case DigestAlgorithm.SHA3_256: return new Sha3Digest(256); case DigestAlgorithm.SHA3_384: return new Sha3Digest(384); case DigestAlgorithm.SHA3_512: return new Sha3Digest(512); + case DigestAlgorithm.SHAKE128: return new ShakeDigest(128); + case DigestAlgorithm.SHAKE256: return new ShakeDigest(256); case DigestAlgorithm.TIGER: return new TigerDigest(); case DigestAlgorithm.WHIRLPOOL: return new WhirlpoolDigest(); } diff --git a/crypto/src/security/DotNetUtilities.cs b/crypto/src/security/DotNetUtilities.cs
index d50e17d39..732b5e075 100644 --- a/crypto/src/security/DotNetUtilities.cs +++ b/crypto/src/security/DotNetUtilities.cs
@@ -233,7 +233,9 @@ namespace Org.BouncyCastle.Security private static RSA CreateRSAProvider(RSAParameters rp) { - RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(); + CspParameters csp = new CspParameters(); + csp.KeyContainerName = string.Format("BouncyCastle-{0}", Guid.NewGuid()); + RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(csp); rsaCsp.ImportParameters(rp); return rsaCsp; } diff --git a/crypto/src/security/PrivateKeyFactory.cs b/crypto/src/security/PrivateKeyFactory.cs
index c5ddd5d78..edc5ef85a 100644 --- a/crypto/src/security/PrivateKeyFactory.cs +++ b/crypto/src/security/PrivateKeyFactory.cs
@@ -15,6 +15,7 @@ using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Security { @@ -43,7 +44,7 @@ namespace Org.BouncyCastle.Security public static AsymmetricKeyParameter CreateKey( PrivateKeyInfo keyInfo) { - AlgorithmIdentifier algID = keyInfo.AlgorithmID; + AlgorithmIdentifier algID = keyInfo.PrivateKeyAlgorithm; DerObjectIdentifier algOid = algID.ObjectID; // TODO See RSAUtil.isRsaOid in Java build @@ -52,8 +53,7 @@ namespace Org.BouncyCastle.Security || algOid.Equals(PkcsObjectIdentifiers.IdRsassaPss) || algOid.Equals(PkcsObjectIdentifiers.IdRsaesOaep)) { - RsaPrivateKeyStructure keyStructure = new RsaPrivateKeyStructure( - Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + RsaPrivateKeyStructure keyStructure = RsaPrivateKeyStructure.GetInstance(keyInfo.ParsePrivateKey()); return new RsaPrivateCrtKeyParameters( keyStructure.Modulus, @@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Security { DHParameter para = new DHParameter( Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); - DerInteger derX = (DerInteger)keyInfo.PrivateKey; + DerInteger derX = (DerInteger)keyInfo.ParsePrivateKey(); BigInteger lVal = para.L; int l = lVal == null ? 0 : lVal.IntValue; @@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Security { ElGamalParameter para = new ElGamalParameter( Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); - DerInteger derX = (DerInteger)keyInfo.PrivateKey; + DerInteger derX = (DerInteger)keyInfo.ParsePrivateKey(); return new ElGamalPrivateKeyParameters( derX.Value, @@ -91,7 +91,7 @@ namespace Org.BouncyCastle.Security } else if (algOid.Equals(X9ObjectIdentifiers.IdDsa)) { - DerInteger derX = (DerInteger) keyInfo.PrivateKey; + DerInteger derX = (DerInteger)keyInfo.ParsePrivateKey(); Asn1Encodable ae = algID.Parameters; DsaParameters parameters = null; @@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Security } ECPrivateKeyStructure ec = new ECPrivateKeyStructure( - Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey())); BigInteger d = ec.GetKey(); if (para.IsNamedCurve) @@ -134,13 +134,23 @@ namespace Org.BouncyCastle.Security Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); - ECPrivateKeyStructure ec = new ECPrivateKeyStructure( - Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + Asn1Object privKey = keyInfo.ParsePrivateKey(); + ECPrivateKeyStructure ec; + + if (privKey is DerInteger) + { + // TODO Do we need to pass any parameters here? + ec = new ECPrivateKeyStructure(((DerInteger)privKey).Value); + } + else + { + ec = ECPrivateKeyStructure.GetInstance(privKey); + } ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); if (ecP == null) - return null; + throw new ArgumentException("Unrecognized curve OID for GostR3410x2001 private key"); return new ECPrivateKeyParameters("ECGOST3410", ec.GetKey(), gostParams.PublicKeyParamSet); } @@ -149,16 +159,8 @@ namespace Org.BouncyCastle.Security 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); + DerOctetString derX = (DerOctetString)keyInfo.ParsePrivateKey(); + BigInteger x = new BigInteger(1, Arrays.Reverse(derX.GetOctets())); return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet); } diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index ac9d98158..137a471c1 100644 --- a/crypto/src/security/SecureRandom.cs +++ b/crypto/src/security/SecureRandom.cs
@@ -1,4 +1,5 @@ using System; +using System.Threading; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; @@ -8,221 +9,256 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Security { public class SecureRandom - : Random + : Random { - // Note: all objects of this class should be deriving their random data from - // a single generator appropriate to the digest being used. - private static readonly IRandomGenerator sha1Generator = new DigestRandomGenerator(new Sha1Digest()); - private static readonly IRandomGenerator sha256Generator = new DigestRandomGenerator(new Sha256Digest()); - - private static readonly SecureRandom[] master = { null }; - private static SecureRandom Master - { - get - { - if (master[0] == null) - { - IRandomGenerator gen = sha256Generator; - 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? - string drgName = Platform.ToUpperInvariant(algorithm); - - IRandomGenerator drg = null; - if (drgName == "SHA1PRNG") - { - drg = sha1Generator; - } - else if (drgName == "SHA256PRNG") - { - drg = sha256Generator; - } - - if (drg != null) - { - return new SecureRandom(drg); - } - - throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); - } - - public static byte[] GetSeed( - int length) - { - return Master.GenerateSeed(length); - } - - protected IRandomGenerator generator; - - public SecureRandom() - : this(sha1Generator) - { - SetSeed(GetSeed(8)); - } - - public SecureRandom( - byte[] inSeed) - : this(sha1Generator) - { - SetSeed(inSeed); - } - - /// <summary>Use the specified instance of IRandomGenerator as random source.</summary> - /// <remarks> - /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the - /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide - /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c> - /// implementation. - /// </remarks> - /// <param name="generator">The source to generate all random bytes from.</param> - 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", "cannot be negative"); - - 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]; + private static long counter = Times.NanoTime(); + +#if NETCF_1_0 + private static object counterLock = new object(); + private static long NextCounterValue() + { + lock (counterLock) + { + return ++counter; + } + } + + private static readonly SecureRandom[] master = { null }; + private static SecureRandom Master + { + get + { + lock (master) + { + if (master[0] == null) + { + SecureRandom sr = master[0] = GetInstance("SHA256PRNG", false); + + // Even though Ticks has at most 8 or 14 bits of entropy, there's no harm in adding it. + sr.SetSeed(DateTime.Now.Ticks); + + // 32 will be enough when ThreadedSeedGenerator is fixed. Until then, ThreadedSeedGenerator returns low + // entropy, and this is not sufficient to be secure. http://www.bouncycastle.org/csharpdevmailarchive/msg00814.html + sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(32, true)); + } + + return master[0]; + } + } + } +#else + private static long NextCounterValue() + { + return Interlocked.Increment(ref counter); + } + + private static readonly SecureRandom master = new SecureRandom(new CryptoApiRandomGenerator()); + private static SecureRandom Master + { + get { return master; } + } +#endif + + private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed) + { + IDigest digest = DigestUtilities.GetDigest(digestName); + if (digest == null) + return null; + DigestRandomGenerator prng = new DigestRandomGenerator(digest); + if (autoSeed) + { + prng.AddSeedMaterial(NextCounterValue()); + prng.AddSeedMaterial(GetSeed(digest.GetDigestSize())); + } + return prng; + } + + /// <summary> + /// Create and auto-seed an instance based on the given algorithm. + /// </summary> + /// <remarks>Equivalent to GetInstance(algorithm, true)</remarks> + /// <param name="algorithm">e.g. "SHA256PRNG"</param> + public static SecureRandom GetInstance(string algorithm) + { + return GetInstance(algorithm, true); + } + + /// <summary> + /// Create an instance based on the given algorithm, with optional auto-seeding + /// </summary> + /// <param name="algorithm">e.g. "SHA256PRNG"</param> + /// <param name="autoSeed">If true, the instance will be auto-seeded.</param> + public static SecureRandom GetInstance(string algorithm, bool autoSeed) + { + string upper = Platform.ToUpperInvariant(algorithm); + if (upper.EndsWith("PRNG")) + { + string digestName = upper.Substring(0, upper.Length - "PRNG".Length); + DigestRandomGenerator prng = CreatePrng(digestName, autoSeed); + if (prng != null) + { + return new SecureRandom(prng); + } + } + + throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); + } + + public static byte[] GetSeed(int length) + { +#if NETCF_1_0 + lock (master) +#endif + return Master.GenerateSeed(length); + } + + protected readonly IRandomGenerator generator; + + public SecureRandom() + : this(CreatePrng("SHA256", true)) + { + } + + /// <remarks> + /// To replicate existing predictable output, replace with GetInstance("SHA1PRNG", false), followed by SetSeed(seed) + /// </remarks> + [Obsolete("Use GetInstance/SetSeed instead")] + public SecureRandom(byte[] seed) + : this(CreatePrng("SHA1", false)) + { + SetSeed(seed); + } + + /// <summary>Use the specified instance of IRandomGenerator as random source.</summary> + /// <remarks> + /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the + /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide + /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c> + /// implementation. + /// </remarks> + /// <param name="generator">The source to generate all random bytes from.</param> + 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[] seed) + { + generator.AddSeedMaterial(seed); + } + + 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", "cannot be negative"); + + 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[] buf) + { + generator.NextBytes(buf); + } + + public virtual void NextBytes(byte[] buf, int off, int len) + { + generator.NextBytes(buf, off, len); + } + + 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; + int result = 0; for (int i = 0; i < 4; i++) { result = (result << 8) + (intBytes[i] & 0xff); } - return result; + return result; } - public virtual long NextLong() - { - return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); - } + public virtual long NextLong() + { + return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); + } } } diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index 136361532..bd1515147 100644 --- a/crypto/src/security/SignerUtilities.cs +++ b/crypto/src/security/SignerUtilities.cs
@@ -23,16 +23,16 @@ namespace Org.BouncyCastle.Security /// </summary> public sealed class SignerUtilities { - private SignerUtilities() - { - } + private SignerUtilities() + { + } - internal static readonly IDictionary algorithms = Platform.CreateHashtable(); + internal static readonly IDictionary algorithms = Platform.CreateHashtable(); internal static readonly IDictionary oids = Platform.CreateHashtable(); - static SignerUtilities() + static SignerUtilities() { - algorithms["MD2WITHRSA"] = "MD2withRSA"; + algorithms["MD2WITHRSA"] = "MD2withRSA"; algorithms["MD2WITHRSAENCRYPTION"] = "MD2withRSA"; algorithms[PkcsObjectIdentifiers.MD2WithRsaEncryption.Id] = "MD2withRSA"; @@ -69,41 +69,41 @@ namespace Org.BouncyCastle.Security algorithms[PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id] = "SHA-512withRSA"; algorithms["SHA-512WITHRSA"] = "SHA-512withRSA"; - algorithms["PSSWITHRSA"] = "PSSwithRSA"; - algorithms["RSASSA-PSS"] = "PSSwithRSA"; - algorithms[PkcsObjectIdentifiers.IdRsassaPss.Id] = "PSSwithRSA"; - algorithms["RSAPSS"] = "PSSwithRSA"; + algorithms["PSSWITHRSA"] = "PSSwithRSA"; + algorithms["RSASSA-PSS"] = "PSSwithRSA"; + algorithms[PkcsObjectIdentifiers.IdRsassaPss.Id] = "PSSwithRSA"; + algorithms["RSAPSS"] = "PSSwithRSA"; - algorithms["SHA1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1"; - algorithms["SHA-1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1"; - algorithms["SHA1WITHRSA/PSS"] = "SHA-1withRSAandMGF1"; - algorithms["SHA-1WITHRSA/PSS"] = "SHA-1withRSAandMGF1"; + 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["SHA224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; - algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; + algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; algorithms["SHA-256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; - algorithms["SHA256WITHRSA/PSS"] = "SHA-256withRSAandMGF1"; - algorithms["SHA-256WITHRSA/PSS"] = "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["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["SHA512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; + algorithms["SHA-512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; - algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA"; + algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA"; algorithms["RIPEMD128WITHRSAENCRYPTION"] = "RIPEMD128withRSA"; algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128.Id] = "RIPEMD128withRSA"; - algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA"; + algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA"; algorithms["RIPEMD160WITHRSAENCRYPTION"] = "RIPEMD160withRSA"; algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160.Id] = "RIPEMD160withRSA"; @@ -111,126 +111,123 @@ namespace Org.BouncyCastle.Security algorithms["RIPEMD256WITHRSAENCRYPTION"] = "RIPEMD256withRSA"; algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256.Id] = "RIPEMD256withRSA"; - algorithms["NONEWITHRSA"] = "RSA"; - algorithms["RSAWITHNONE"] = "RSA"; - algorithms["RAWRSA"] = "RSA"; - - algorithms["RAWRSAPSS"] = "RAWRSASSA-PSS"; - algorithms["NONEWITHRSAPSS"] = "RAWRSASSA-PSS"; - algorithms["NONEWITHRSASSA-PSS"] = "RAWRSASSA-PSS"; - - algorithms["NONEWITHDSA"] = "NONEwithDSA"; - algorithms["DSAWITHNONE"] = "NONEwithDSA"; - algorithms["RAWDSA"] = "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["NONEWITHRSA"] = "RSA"; + algorithms["RSAWITHNONE"] = "RSA"; + algorithms["RAWRSA"] = "RSA"; + + algorithms["RAWRSAPSS"] = "RAWRSASSA-PSS"; + algorithms["NONEWITHRSAPSS"] = "RAWRSASSA-PSS"; + algorithms["NONEWITHRSASSA-PSS"] = "RAWRSASSA-PSS"; + + algorithms["NONEWITHDSA"] = "NONEwithDSA"; + algorithms["DSAWITHNONE"] = "NONEwithDSA"; + algorithms["RAWDSA"] = "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["NONEWITHECDSA"] = "NONEwithECDSA"; - algorithms["ECDSAWITHNONE"] = "NONEwithECDSA"; - - 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["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["NONEWITHECDSA"] = "NONEwithECDSA"; + algorithms["ECDSAWITHNONE"] = "NONEwithECDSA"; + + 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; + 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["ECDSAWITHRIPEMD160"] = "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; @@ -240,129 +237,129 @@ namespace Org.BouncyCastle.Security 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["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["RIPEMD128withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128; + oids["RIPEMD160withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160; + oids["RIPEMD256withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256; - oids["SHA-1withDSA"] = X9ObjectIdentifiers.IdDsaWithSha1; + 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["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; - } + oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; + oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + } - /// <summary> - /// Returns a ObjectIdentifier for a give encoding. + /// <summary> + /// Returns an ObjectIdentifier for a given encoding. /// </summary> /// <param name="mechanism">A string representation of the encoding.</param> - /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns> - // TODO Don't really want to support this + /// <returns>A DerObjectIdentifier, null if the OID is not available.</returns> + // TODO Don't really want to support this public static DerObjectIdentifier GetObjectIdentifier( - string mechanism) + string mechanism) { - if (mechanism == null) - throw new ArgumentNullException("mechanism"); + if (mechanism == null) + throw new ArgumentNullException("mechanism"); - mechanism = Platform.ToUpperInvariant(mechanism); - string aliased = (string) algorithms[mechanism]; + mechanism = Platform.ToUpperInvariant(mechanism); + string aliased = (string) algorithms[mechanism]; - if (aliased != null) - mechanism = aliased; + if (aliased != null) + mechanism = aliased; - return (DerObjectIdentifier) oids[mechanism]; - } + return (DerObjectIdentifier) oids[mechanism]; + } - public static ICollection Algorithms + public static ICollection Algorithms { get { return oids.Keys; } } - public static Asn1Encodable GetDefaultX509Parameters( - DerObjectIdentifier id) - { - return GetDefaultX509Parameters(id.Id); - } - - public static Asn1Encodable GetDefaultX509Parameters( - string algorithm) - { - if (algorithm == null) - throw new ArgumentNullException("algorithm"); - - algorithm = Platform.ToUpperInvariant(algorithm); - - string mechanism = (string) algorithms[algorithm]; - - if (mechanism == null) - mechanism = algorithm; - - if (mechanism == "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 GetPssX509Parameters("SHA-1"); - } - - if (mechanism.EndsWith("withRSAandMGF1")) - { - string digestName = mechanism.Substring(0, mechanism.Length - "withRSAandMGF1".Length); - return GetPssX509Parameters(digestName); - } - - return DerNull.Instance; - } - - private static Asn1Encodable GetPssX509Parameters( - string digestName) - { - AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier( - DigestUtilities.GetObjectIdentifier(digestName), DerNull.Instance); - - // TODO Is it possible for the MGF hash alg to be different from the PSS one? - AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier( - PkcsObjectIdentifiers.IdMgf1, hashAlgorithm); - - int saltLen = DigestUtilities.GetDigest(digestName).GetDigestSize(); - return new RsassaPssParameters(hashAlgorithm, maskGenAlgorithm, - new DerInteger(saltLen), new DerInteger(1)); - } - - public static ISigner GetSigner( - DerObjectIdentifier id) + public static Asn1Encodable GetDefaultX509Parameters( + DerObjectIdentifier id) + { + return GetDefaultX509Parameters(id.Id); + } + + public static Asn1Encodable GetDefaultX509Parameters( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = Platform.ToUpperInvariant(algorithm); + + string mechanism = (string) algorithms[algorithm]; + + if (mechanism == null) + mechanism = algorithm; + + if (mechanism == "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 GetPssX509Parameters("SHA-1"); + } + + if (mechanism.EndsWith("withRSAandMGF1")) + { + string digestName = mechanism.Substring(0, mechanism.Length - "withRSAandMGF1".Length); + return GetPssX509Parameters(digestName); + } + + return DerNull.Instance; + } + + private static Asn1Encodable GetPssX509Parameters( + string digestName) + { + AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier( + DigestUtilities.GetObjectIdentifier(digestName), DerNull.Instance); + + // TODO Is it possible for the MGF hash alg to be different from the PSS one? + AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier( + PkcsObjectIdentifiers.IdMgf1, hashAlgorithm); + + int saltLen = DigestUtilities.GetDigest(digestName).GetDigestSize(); + return new RsassaPssParameters(hashAlgorithm, maskGenAlgorithm, + new DerInteger(saltLen), new DerInteger(1)); + } + + public static ISigner GetSigner( + DerObjectIdentifier id) { return GetSigner(id.Id); } - public static ISigner GetSigner( - string algorithm) + public static ISigner GetSigner( + string algorithm) { - if (algorithm == null) - throw new ArgumentNullException("algorithm"); + if (algorithm == null) + throw new ArgumentNullException("algorithm"); algorithm = Platform.ToUpperInvariant(algorithm); - string mechanism = (string) algorithms[algorithm]; + string mechanism = (string) algorithms[algorithm]; - if (mechanism == null) - mechanism = algorithm; + if (mechanism == null) + mechanism = algorithm; - if (mechanism.Equals("RSA")) - { - return (new RsaDigestSigner(new NullDigest())); - } - if (mechanism.Equals("MD2withRSA")) + if (mechanism.Equals("RSA")) + { + return (new RsaDigestSigner(new NullDigest(), (AlgorithmIdentifier)null)); + } + if (mechanism.Equals("MD2withRSA")) { return (new RsaDigestSigner(new MD2Digest())); } @@ -394,7 +391,7 @@ namespace Org.BouncyCastle.Security { return (new RsaDigestSigner(new Sha512Digest())); } - if (mechanism.Equals("RIPEMD128withRSA")) + if (mechanism.Equals("RIPEMD128withRSA")) { return (new RsaDigestSigner(new RipeMD128Digest())); } @@ -407,141 +404,161 @@ namespace Org.BouncyCastle.Security return (new RsaDigestSigner(new RipeMD256Digest())); } - if (mechanism.Equals("RAWRSASSA-PSS")) - { - // TODO Add support for other parameter settings - return PssSigner.CreateRawSigner(new RsaBlindedEngine(), new Sha1Digest()); - } - 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")) + if (mechanism.Equals("RAWRSASSA-PSS")) + { + // TODO Add support for other parameter settings + return PssSigner.CreateRawSigner(new RsaBlindedEngine(), new Sha1Digest()); + } + 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("NONEwithECDSA")) - { - return (new DsaDigestSigner(new ECDsaSigner(), new NullDigest())); - } - if (mechanism.Equals("SHA-1withECDSA")) + 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("NONEwithECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new NullDigest())); + } + 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."); + 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); + } + + if (mechanism.EndsWith("/X9.31")) + { + string x931 = mechanism.Substring(0, mechanism.Length - "/X9.31".Length); + int withPos = x931.IndexOf("WITH"); + if (withPos > 0) + { + int endPos = withPos + "WITH".Length; + + string digestName = x931.Substring(0, withPos); + IDigest digest = DigestUtilities.GetDigest(digestName); + + string cipherName = x931.Substring(endPos, x931.Length - endPos); + if (cipherName.Equals("RSA")) + { + IAsymmetricBlockCipher cipher = new RsaBlindedEngine(); + return new X931Signer(cipher, digest); + } + } + } + + throw new SecurityUtilityException("Signer " + algorithm + " not recognised."); } public static string GetEncodingName( - DerObjectIdentifier oid) + DerObjectIdentifier oid) { return (string) algorithms[oid.Id]; } diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index a21dd00b1..1f9711555 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs
@@ -1,6 +1,8 @@ using System; using System.Text; +using Org.BouncyCastle.Math; + namespace Org.BouncyCastle.Utilities { /// <summary> General array utilities.</summary> @@ -309,6 +311,48 @@ namespace Org.BouncyCastle.Utilities return hc; } + [CLSCompliantAttribute(false)] + public static int GetHashCode(ulong[] data) + { + if (data == null) + return 0; + + int i = data.Length; + int hc = i + 1; + + while (--i >= 0) + { + ulong di = data[i]; + hc *= 257; + hc ^= (int)di; + hc *= 257; + hc ^= (int)(di >> 32); + } + + return hc; + } + + [CLSCompliantAttribute(false)] + public static int GetHashCode(ulong[] data, int off, int len) + { + if (data == null) + return 0; + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + ulong di = data[off + i]; + hc *= 257; + hc ^= (int)di; + hc *= 257; + hc ^= (int)(di >> 32); + } + + return hc; + } + public static byte[] Clone( byte[] data) { @@ -337,6 +381,11 @@ namespace Org.BouncyCastle.Utilities return data == null ? null : (int[])data.Clone(); } + internal static uint[] Clone(uint[] data) + { + return data == null ? null : (uint[])data.Clone(); + } + public static long[] Clone(long[] data) { return data == null ? null : (long[])data.Clone(); @@ -366,6 +415,36 @@ namespace Org.BouncyCastle.Utilities return existing; } + public static bool Contains(byte[] a, byte n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static bool Contains(short[] a, short n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static bool Contains(int[] a, int n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + public static void Fill( byte[] buf, byte b) @@ -377,10 +456,125 @@ namespace Org.BouncyCastle.Utilities } } - public static byte[] Copy(byte[] data, int off, int len) + public static byte[] CopyOf(byte[] data, int newLength) + { + byte[] tmp = new byte[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static char[] CopyOf(char[] data, int newLength) + { + char[] tmp = new char[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static int[] CopyOf(int[] data, int newLength) + { + int[] tmp = new int[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static long[] CopyOf(long[] data, int newLength) + { + long[] tmp = new long[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static BigInteger[] CopyOf(BigInteger[] data, int newLength) + { + BigInteger[] tmp = new BigInteger[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + /** + * Make a copy of a range of bytes from the passed in data array. The range can + * extend beyond the end of the input array, in which case the return array will + * be padded with zeroes. + * + * @param data the array from which the data is to be copied. + * @param from the start index at which the copying should take place. + * @param to the final index of the range (exclusive). + * + * @return a new byte array containing the range given. + */ + public static byte[] CopyOfRange(byte[] data, int from, int to) + { + int newLength = GetLength(from, to); + byte[] tmp = new byte[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + public static int[] CopyOfRange(int[] data, int from, int to) + { + int newLength = GetLength(from, to); + int[] tmp = new int[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + public static long[] CopyOfRange(long[] data, int from, int to) { - byte[] result = new byte[len]; - Array.Copy(data, off, result, 0, len); + int newLength = GetLength(from, to); + long[] tmp = new long[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + public static BigInteger[] CopyOfRange(BigInteger[] data, int from, int to) + { + int newLength = GetLength(from, to); + BigInteger[] tmp = new BigInteger[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + private static int GetLength(int from, int to) + { + int newLength = to - from; + if (newLength < 0) + throw new ArgumentException(from + " > " + to); + return newLength; + } + + public static byte[] Append(byte[] a, byte b) + { + if (a == null) + return new byte[] { b }; + + int length = a.Length; + byte[] result = new byte[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static short[] Append(short[] a, short b) + { + if (a == null) + return new short[] { b }; + + int length = a.Length; + short[] result = new short[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static int[] Append(int[] a, int b) + { + if (a == null) + return new int[] { b }; + + int length = a.Length; + int[] result = new int[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; return result; } @@ -396,5 +590,86 @@ namespace Org.BouncyCastle.Utilities Array.Copy(b, 0, rv, a.Length, b.Length); return rv; } + + public static int[] Concatenate(int[] a, int[] b) + { + if (a == null) + return Clone(b); + if (b == null) + return Clone(a); + + int[] rv = new int[a.Length + b.Length]; + Array.Copy(a, 0, rv, 0, a.Length); + Array.Copy(b, 0, rv, a.Length, b.Length); + return rv; + } + + public static byte[] Prepend(byte[] a, byte b) + { + if (a == null) + return new byte[] { b }; + + int length = a.Length; + byte[] result = new byte[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static short[] Prepend(short[] a, short b) + { + if (a == null) + return new short[] { b }; + + int length = a.Length; + short[] result = new short[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static int[] Prepend(int[] a, int b) + { + if (a == null) + return new int[] { b }; + + int length = a.Length; + int[] result = new int[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static byte[] Reverse(byte[] a) + { + if (a == null) + return null; + + int p1 = 0, p2 = a.Length; + byte[] result = new byte[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } + + public static int[] Reverse(int[] a) + { + if (a == null) + return null; + + int p1 = 0, p2 = a.Length; + int[] result = new int[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } } } diff --git a/crypto/src/util/Times.cs b/crypto/src/util/Times.cs new file mode 100644
index 000000000..99a78d21a --- /dev/null +++ b/crypto/src/util/Times.cs
@@ -0,0 +1,14 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + public sealed class Times + { + private static long NanosecondsPerTick = 100L; + + public static long NanoTime() + { + return DateTime.UtcNow.Ticks * NanosecondsPerTick; + } + } +} diff --git a/crypto/src/util/io/FilterStream.cs b/crypto/src/util/io/FilterStream.cs new file mode 100644
index 000000000..260ce1789 --- /dev/null +++ b/crypto/src/util/io/FilterStream.cs
@@ -0,0 +1,66 @@ +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class FilterStream : Stream + { + public FilterStream(Stream s) + { + this.s = s; + } + public override bool CanRead + { + get { return s.CanRead; } + } + public override bool CanSeek + { + get { return s.CanSeek; } + } + public override bool CanWrite + { + get { return s.CanWrite; } + } + public override long Length + { + get { return s.Length; } + } + public override long Position + { + get { return s.Position; } + set { s.Position = value; } + } + public override void Close() + { + s.Close(); + } + public override void Flush() + { + s.Flush(); + } + public override long Seek(long offset, SeekOrigin origin) + { + return s.Seek(offset, origin); + } + public override void SetLength(long value) + { + s.SetLength(value); + } + public override int Read(byte[] buffer, int offset, int count) + { + return s.Read(buffer, offset, count); + } + public override int ReadByte() + { + return s.ReadByte(); + } + public override void Write(byte[] buffer, int offset, int count) + { + s.Write(buffer, offset, count); + } + public override void WriteByte(byte value) + { + s.WriteByte(value); + } + protected readonly Stream s; + } +} diff --git a/crypto/src/util/io/Streams.cs b/crypto/src/util/io/Streams.cs
index ee95d3b01..70957acc7 100644 --- a/crypto/src/util/io/Streams.cs +++ b/crypto/src/util/io/Streams.cs
@@ -83,10 +83,10 @@ namespace Org.BouncyCastle.Utilities.IO int numRead; while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0) { - total += numRead; - if (total > limit) + if ((limit - total) < numRead) throw new StreamOverflowException("Data Overflow"); - outStr.Write(bs, 0, numRead); + total += numRead; + outStr.Write(bs, 0, numRead); } return total; } diff --git a/crypto/src/x509/X509Certificate.cs b/crypto/src/x509/X509Certificate.cs
index f156f3147..4487232f0 100644 --- a/crypto/src/x509/X509Certificate.cs +++ b/crypto/src/x509/X509Certificate.cs
@@ -14,6 +14,7 @@ using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.X509.Extension; +using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.X509 { @@ -546,30 +547,38 @@ namespace Org.BouncyCastle.X509 public virtual void Verify( AsymmetricKeyParameter key) { - string sigName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm); - ISigner signature = SignerUtilities.GetSigner(sigName); - - CheckSignature(key, signature); + CheckSignature(new Asn1SignatureVerifier(c.SignatureAlgorithm, key)); } - protected virtual void CheckSignature( - AsymmetricKeyParameter publicKey, - ISigner signature) + /// <summary> + /// Verify the certificate's signature using a verifier created using the passed in verifier provider. + /// </summary> + /// <param name="verifierProvider">An appropriate provider for verifying the certificate's signature.</param> + /// <returns>True if the signature is valid.</returns> + /// <exception cref="Exception">If verifier provider is not appropriate or the certificate algorithm is invalid.</exception> + public virtual void Verify( + ISignatureVerifierProvider verifierProvider) + { + CheckSignature(verifierProvider.CreateSignatureVerifier (c.SignatureAlgorithm)); + } + + protected virtual void CheckSignature( + ISignatureVerifier verifier) { if (!IsAlgIDEqual(c.SignatureAlgorithm, 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); + IStreamCalculator streamCalculator = verifier.CreateCalculator(); byte[] b = this.GetTbsCertificate(); - signature.BlockUpdate(b, 0, b.Length); - byte[] sig = this.GetSignature(); - if (!signature.VerifySignature(sig)) + streamCalculator.Stream.Write(b, 0, b.Length); + + streamCalculator.Stream.Close(); + + if (!((IVerifier)streamCalculator.GetResult()).IsVerified(this.GetSignature())) { throw new InvalidKeyException("Public key presented not for certificate signature"); } diff --git a/crypto/src/x509/X509Crl.cs b/crypto/src/x509/X509Crl.cs
index 7d0e7aa72..1746960fb 100644 --- a/crypto/src/x509/X509Crl.cs +++ b/crypto/src/x509/X509Crl.cs
@@ -14,6 +14,7 @@ using Org.BouncyCastle.Utilities.Collections; using Org.BouncyCastle.Utilities.Date; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.X509.Extension; +using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.X509 { @@ -83,24 +84,46 @@ namespace Org.BouncyCastle.X509 public virtual void Verify( AsymmetricKeyParameter publicKey) { - if (!c.SignatureAlgorithm.Equals(c.TbsCertList.Signature)) - { - throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList."); - } + Verify(new Asn1SignatureVerifierProvider(publicKey)); + } - ISigner sig = SignerUtilities.GetSigner(SigAlgName); - sig.Init(false, publicKey); + /// <summary> + /// Verify the CRL's signature using a verifier created using the passed in verifier provider. + /// </summary> + /// <param name="verifierProvider">An appropriate provider for verifying the CRL's signature.</param> + /// <returns>True if the signature is valid.</returns> + /// <exception cref="Exception">If verifier provider is not appropriate or the CRL algorithm is invalid.</exception> + public virtual void Verify( + ISignatureVerifierProvider verifierProvider) + { + CheckSignature(verifierProvider.CreateSignatureVerifier(c.SignatureAlgorithm)); + } - byte[] encoded = this.GetTbsCertList(); - sig.BlockUpdate(encoded, 0, encoded.Length); + protected virtual void CheckSignature( + ISignatureVerifier verifier) + { + if (!c.SignatureAlgorithm.Equals(c.TbsCertList.Signature)) + { + throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList."); + } - if (!sig.VerifySignature(this.GetSignature())) - { - throw new SignatureException("CRL does not verify with supplied public key."); - } - } + Asn1Encodable parameters = c.SignatureAlgorithm.Parameters; + + IStreamCalculator streamCalculator = verifier.CreateCalculator(); + + byte[] b = this.GetTbsCertList(); + + streamCalculator.Stream.Write(b, 0, b.Length); + + streamCalculator.Stream.Close(); + + if (!((IVerifier)streamCalculator.GetResult()).IsVerified(this.GetSignature())) + { + throw new InvalidKeyException("CRL does not verify with supplied public key."); + } + } - public virtual int Version + public virtual int Version { get { return c.Version; } } diff --git a/crypto/src/x509/X509V1CertificateGenerator.cs b/crypto/src/x509/X509V1CertificateGenerator.cs
index 02b58a198..a452df440 100644 --- a/crypto/src/x509/X509V1CertificateGenerator.cs +++ b/crypto/src/x509/X509V1CertificateGenerator.cs
@@ -1,10 +1,12 @@ using System; +using System.IO; using System.Collections; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Security.Certificates; @@ -119,6 +121,7 @@ namespace Org.BouncyCastle.X509 /// This can be either a name or an OID, names are treated as case insensitive. /// </summary> /// <param name="signatureAlgorithm">string representation of the algorithm name</param> + [Obsolete("Not needed if Generate used with an ISignatureCalculator")] public void SetSignatureAlgorithm( string signatureAlgorithm) { @@ -143,6 +146,7 @@ namespace Org.BouncyCastle.X509 /// </summary> /// <param name="privateKey">The private key of the issuer used to sign this certificate.</param> /// <returns>An X509Certificate.</returns> + [Obsolete("Use Generate with an ISignatureCalculator")] public X509Certificate Generate( AsymmetricKeyParameter privateKey) { @@ -155,43 +159,43 @@ namespace Org.BouncyCastle.X509 /// <param name="privateKey">The private key of the issuer used to sign this certificate.</param> /// <param name="random">The Secure Random you want to use.</param> /// <returns>An X509Certificate.</returns> + [Obsolete("Use Generate with an ISignatureCalculator")] public X509Certificate Generate( AsymmetricKeyParameter privateKey, SecureRandom random) { + return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random)); + } + + /// <summary> + /// Generate a new X509Certificate using the passed in SignatureCalculator. + /// </summary> + /// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param> + /// <returns>An X509Certificate.</returns> + public X509Certificate Generate(ISignatureCalculator signatureCalculator) + { + tbsGen.SetSignature ((AlgorithmIdentifier)signatureCalculator.AlgorithmDetails); + 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); - } + IStreamCalculator streamCalculator = signatureCalculator.CreateCalculator(); - 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); - } + byte[] encoded = tbsCert.GetDerEncoded(); + + streamCalculator.Stream.Write(encoded, 0, encoded.Length); + + streamCalculator.Stream.Close(); + + return GenerateJcaObject(tbsCert, (AlgorithmIdentifier)signatureCalculator.AlgorithmDetails, ((IBlockResult)streamCalculator.GetResult()).DoFinal()); } private X509Certificate GenerateJcaObject( TbsCertificateStructure tbsCert, + AlgorithmIdentifier sigAlg, byte[] signature) { return new X509Certificate( - new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature))); + new X509CertificateStructure(tbsCert, sigAlg, new DerBitString(signature))); } /// <summary> diff --git a/crypto/src/x509/X509V2AttributeCertificate.cs b/crypto/src/x509/X509V2AttributeCertificate.cs
index 117ac4cc2..cc72c23bb 100644 --- a/crypto/src/x509/X509V2AttributeCertificate.cs +++ b/crypto/src/x509/X509V2AttributeCertificate.cs
@@ -9,6 +9,7 @@ using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.X509 { @@ -151,29 +152,48 @@ namespace Org.BouncyCastle.X509 return cert.SignatureValue.GetBytes(); } - public virtual void Verify( - AsymmetricKeyParameter publicKey) - { - if (!cert.SignatureAlgorithm.Equals(cert.ACInfo.Signature)) + public virtual void Verify( + AsymmetricKeyParameter key) + { + CheckSignature(new Asn1SignatureVerifier(cert.SignatureAlgorithm, key)); + } + + /// <summary> + /// Verify the certificate's signature using a verifier created using the passed in verifier provider. + /// </summary> + /// <param name="verifierProvider">An appropriate provider for verifying the certificate's signature.</param> + /// <returns>True if the signature is valid.</returns> + /// <exception cref="Exception">If verifier provider is not appropriate or the certificate algorithm is invalid.</exception> + public virtual void Verify( + ISignatureVerifierProvider verifierProvider) + { + CheckSignature(verifierProvider.CreateSignatureVerifier(cert.SignatureAlgorithm)); + } + + protected virtual void CheckSignature( + ISignatureVerifier verifier) + { + 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); + IStreamCalculator streamCalculator = verifier.CreateCalculator(); try { - byte[] b = cert.ACInfo.GetEncoded(); - signature.BlockUpdate(b, 0, b.Length); - } + byte[] b = this.cert.ACInfo.GetEncoded(); + + streamCalculator.Stream.Write(b, 0, b.Length); + + streamCalculator.Stream.Close(); + } catch (IOException e) { throw new SignatureException("Exception encoding certificate info object", e); } - if (!signature.VerifySignature(this.GetSignature())) + if (!((IVerifier)streamCalculator.GetResult()).IsVerified(this.GetSignature())) { throw new InvalidKeyException("Public key presented not for certificate signature"); } diff --git a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
index a683d5e20..138f2ec6f 100644 --- a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs +++ b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
@@ -8,6 +8,8 @@ using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Crypto.Operators; +using System.IO; namespace Org.BouncyCastle.X509 { @@ -66,12 +68,13 @@ namespace Org.BouncyCastle.X509 acInfoGen.SetEndDate(new DerGeneralizedTime(date)); } - /// <summary> - /// Set the signature algorithm. This can be either a name or an OID, names - /// are treated as case insensitive. - /// </summary> - /// <param name="signatureAlgorithm">The algorithm name.</param> - public void SetSignatureAlgorithm( + /// <summary> + /// Set the signature algorithm. This can be either a name or an OID, names + /// are treated as case insensitive. + /// </summary> + /// <param name="signatureAlgorithm">The algorithm name.</param> + [Obsolete("Not needed if Generate used with an ISignatureCalculator")] + public void SetSignatureAlgorithm( string signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; @@ -127,37 +130,57 @@ namespace Org.BouncyCastle.X509 extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); } - /// <summary> - /// Generate an X509 certificate, based on the current issuer and subject. - /// </summary> - public IX509AttributeCertificate Generate( - AsymmetricKeyParameter publicKey) + /// <summary> + /// Generate an X509 certificate, based on the current issuer and subject. + /// </summary> + [Obsolete("Use Generate with an ISignatureCalculator")] + public IX509AttributeCertificate Generate( + AsymmetricKeyParameter privateKey) { - return Generate(publicKey, null); + return Generate(privateKey, null); } - /// <summary> - /// Generate an X509 certificate, based on the current issuer and subject, - /// using the supplied source of randomness, if required. - /// </summary> - public IX509AttributeCertificate Generate( - AsymmetricKeyParameter publicKey, + /// <summary> + /// Generate an X509 certificate, based on the current issuer and subject, + /// using the supplied source of randomness, if required. + /// </summary> + [Obsolete("Use Generate with an ISignatureCalculator")] + public IX509AttributeCertificate Generate( + AsymmetricKeyParameter privateKey, SecureRandom random) - { - if (!extGenerator.IsEmpty) + { + return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random)); + } + + /// <summary> + /// Generate a new X.509 Attribute Certificate using the passed in SignatureCalculator. + /// </summary> + /// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param> + /// <returns>An IX509AttributeCertificate.</returns> + public IX509AttributeCertificate Generate(ISignatureCalculator signatureCalculator) + { + if (!extGenerator.IsEmpty) { acInfoGen.SetExtensions(extGenerator.Generate()); } AttributeCertificateInfo acInfo = acInfoGen.GenerateAttributeCertificateInfo(); - Asn1EncodableVector v = new Asn1EncodableVector(); + byte[] encoded = acInfo.GetDerEncoded(); + + IStreamCalculator streamCalculator = signatureCalculator.CreateCalculator(); + + streamCalculator.Stream.Write(encoded, 0, encoded.Length); + + streamCalculator.Stream.Close(); + + Asn1EncodableVector v = new Asn1EncodableVector(); - v.Add(acInfo, sigAlgId); + v.Add(acInfo, (AlgorithmIdentifier)signatureCalculator.AlgorithmDetails); try { - v.Add(new DerBitString(X509Utilities.GetSignatureForObject(sigOID, signatureAlgorithm, publicKey, random, acInfo))); + v.Add(new DerBitString(((IBlockResult)streamCalculator.GetResult()).DoFinal())); return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(new DerSequence(v))); } diff --git a/crypto/src/x509/X509V2CRLGenerator.cs b/crypto/src/x509/X509V2CRLGenerator.cs
index a2293b333..c1cc8e824 100644 --- a/crypto/src/x509/X509V2CRLGenerator.cs +++ b/crypto/src/x509/X509V2CRLGenerator.cs
@@ -10,6 +10,7 @@ using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.X509 { @@ -129,13 +130,12 @@ namespace Org.BouncyCastle.X509 } } - /** - * 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( + /// <summary> + /// Set the signature algorithm that will be used to sign this CRL. + /// </summary> + /// <param name="signatureAlgorithm"/> + [Obsolete("Not needed if Generate used with an ISignatureCalculator")] + public void SetSignatureAlgorithm( string signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; @@ -198,40 +198,55 @@ namespace Org.BouncyCastle.X509 extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue)); } - /// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary> - /// <param name="privateKey">The key used for signing.</param> - public X509Crl Generate( - AsymmetricKeyParameter privateKey) - { - return Generate(privateKey, null); - } + /// <summary> + /// Generate an X.509 CRL, based on the current issuer and subject. + /// </summary> + /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param> + /// <returns>An X509Crl.</returns> + [Obsolete("Use Generate with an ISignatureCalculator")] + public X509Crl Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } - /// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary> - /// <param name="privateKey">The key used for signing.</param> - /// <param name="random">A user-defined source of randomness.</param> - public X509Crl Generate( - AsymmetricKeyParameter privateKey, - SecureRandom random) - { - TbsCertificateList tbsCrl = GenerateCertList(); - byte[] signature; + /// <summary> + /// Generate an X.509 CRL, based on the current issuer and subject using the specified secure random. + /// </summary> + /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param> + /// <param name="random">Your Secure Random instance.</param> + /// <returns>An X509Crl.</returns> + [Obsolete("Use Generate with an ISignatureCalculator")] + public X509Crl Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random)); + } - 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); - } + /// <summary> + /// Generate a new X509Crl using the passed in SignatureCalculator. + /// </summary> + /// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param> + /// <returns>An X509Crl.</returns> + public X509Crl Generate(ISignatureCalculator signatureCalculator) + { + tbsGen.SetSignature((AlgorithmIdentifier)signatureCalculator.AlgorithmDetails); - return GenerateJcaObject(tbsCrl, signature); - } + TbsCertificateList tbsCertList = GenerateCertList(); + + IStreamCalculator streamCalculator = signatureCalculator.CreateCalculator(); + + byte[] encoded = tbsCertList.GetDerEncoded(); + + streamCalculator.Stream.Write(encoded, 0, encoded.Length); + + streamCalculator.Stream.Close(); + + return GenerateJcaObject(tbsCertList, (AlgorithmIdentifier)signatureCalculator.AlgorithmDetails, ((IBlockResult)streamCalculator.GetResult()).DoFinal()); + } - private TbsCertificateList GenerateCertList() + private TbsCertificateList GenerateCertList() { if (!extGenerator.IsEmpty) { @@ -243,11 +258,12 @@ namespace Org.BouncyCastle.X509 private X509Crl GenerateJcaObject( TbsCertificateList tbsCrl, + AlgorithmIdentifier algId, byte[] signature) { return new X509Crl( CertificateList.GetInstance( - new DerSequence(tbsCrl, sigAlgId, new DerBitString(signature)))); + new DerSequence(tbsCrl, algId, new DerBitString(signature)))); } /// <summary> diff --git a/crypto/src/x509/X509V3CertificateGenerator.cs b/crypto/src/x509/X509V3CertificateGenerator.cs
index bb0dd9cbc..a22cd9943 100644 --- a/crypto/src/x509/X509V3CertificateGenerator.cs +++ b/crypto/src/x509/X509V3CertificateGenerator.cs
@@ -1,9 +1,11 @@ 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.Operators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; @@ -110,6 +112,7 @@ namespace Org.BouncyCastle.X509 /// Set the signature algorithm that will be used to sign this certificate. /// </summary> /// <param name="signatureAlgorithm"/> + [Obsolete("Not needed if Generate used with an ISignatureCalculator")] public void SetSignatureAlgorithm( string signatureAlgorithm) { @@ -274,7 +277,8 @@ namespace Org.BouncyCastle.X509 /// </summary> /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param> /// <returns>An X509Certificate.</returns> - public X509Certificate Generate( + [Obsolete("Use Generate with an ISignatureCalculator")] + public X509Certificate Generate( AsymmetricKeyParameter privateKey) { return Generate(privateKey, null); @@ -286,53 +290,48 @@ namespace Org.BouncyCastle.X509 /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param> /// <param name="random">You Secure Random instance.</param> /// <returns>An X509Certificate.</returns> + [Obsolete("Use Generate with an ISignatureCalculator")] 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); - } + return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random)); } - private TbsCertificateStructure GenerateTbsCert() + /// <summary> + /// Generate a new X509Certificate using the passed in SignatureCalculator. + /// </summary> + /// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param> + /// <returns>An X509Certificate.</returns> + public X509Certificate Generate(ISignatureCalculator signatureCalculator) { - if (!extGenerator.IsEmpty) - { - tbsGen.SetExtensions(extGenerator.Generate()); - } + tbsGen.SetSignature ((AlgorithmIdentifier)signatureCalculator.AlgorithmDetails); + + if (!extGenerator.IsEmpty) + { + tbsGen.SetExtensions(extGenerator.Generate()); + } + + TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate(); + + IStreamCalculator streamCalculator = signatureCalculator.CreateCalculator(); + + byte[] encoded = tbsCert.GetDerEncoded(); + + streamCalculator.Stream.Write (encoded, 0, encoded.Length); + + streamCalculator.Stream.Close (); - return tbsGen.GenerateTbsCertificate(); + return GenerateJcaObject(tbsCert, (AlgorithmIdentifier)signatureCalculator.AlgorithmDetails, ((IBlockResult)streamCalculator.GetResult()).DoFinal()); } private X509Certificate GenerateJcaObject( TbsCertificateStructure tbsCert, + AlgorithmIdentifier sigAlg, byte[] signature) { return new X509Certificate( - new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature))); + new X509CertificateStructure(tbsCert, sigAlg, new DerBitString(signature))); } /// <summary>