summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes2
-rw-r--r--.gitignore15
-rw-r--r--crypto/Contributors.html10
-rw-r--r--crypto/License.html2
-rw-r--r--crypto/NBuild.build3
-rw-r--r--crypto/Readme.html13
-rw-r--r--crypto/crypto.csproj1205
-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
-rw-r--r--crypto/test/data/crypto/SHA3TestVectors.txt837
-rw-r--r--crypto/test/data/crypto/SHAKETestVectors.txt770
-rw-r--r--crypto/test/data/openpgp/bigpub.asc15124
-rw-r--r--crypto/test/data/openpgp/longSigSubPack.asc15
-rw-r--r--crypto/test/data/openpgp/unicode/passphrase_cyr.txt1
-rw-r--r--crypto/test/data/openpgp/unicode/passphrase_for_test.txt1
-rw-r--r--crypto/test/data/openpgp/unicode/secring.gpgbin0 -> 3955 bytes
-rw-r--r--crypto/test/data/openpgp/unicode/test.asc33
-rw-r--r--crypto/test/data/tls/README.txt8
-rw-r--r--crypto/test/data/tls/ca.tmpl4
-rw-r--r--crypto/test/data/tls/client.tmpl5
-rw-r--r--crypto/test/data/tls/server.tmpl5
-rw-r--r--crypto/test/data/tls/x509-ca-key.pem32
-rw-r--r--crypto/test/data/tls/x509-ca.pem21
-rw-r--r--crypto/test/data/tls/x509-client-dsa.pem32
-rw-r--r--crypto/test/data/tls/x509-client-ecdsa.pem17
-rw-r--r--crypto/test/data/tls/x509-client-key-dsa.pem15
-rw-r--r--crypto/test/data/tls/x509-client-key-ecdsa.pem6
-rw-r--r--crypto/test/data/tls/x509-client-key.pem32
-rw-r--r--crypto/test/data/tls/x509-client.pem22
-rw-r--r--crypto/test/data/tls/x509-server-dsa.pem32
-rw-r--r--crypto/test/data/tls/x509-server-ecdsa.pem17
-rw-r--r--crypto/test/data/tls/x509-server-key-dsa.pem15
-rw-r--r--crypto/test/data/tls/x509-server-key-ecdsa.pem6
-rw-r--r--crypto/test/data/tls/x509-server-key.pem32
-rw-r--r--crypto/test/data/tls/x509-server.pem22
-rw-r--r--crypto/test/lib/nunit.core.dllbin90112 -> 139264 bytes
-rw-r--r--crypto/test/lib/nunit.core.interfaces.dllbin40960 -> 61440 bytes
-rw-r--r--crypto/test/lib/nunit.framework.dllbin81920 -> 139264 bytes
-rw-r--r--crypto/test/src/asn1/test/AllTests.cs28
-rw-r--r--crypto/test/src/cms/test/AllTests.cs33
-rw-r--r--crypto/test/src/cms/test/CMSTestUtil.cs37
-rw-r--r--crypto/test/src/cms/test/MiscDataStreamTest.cs15
-rw-r--r--crypto/test/src/cms/test/SignedDataStreamTest.cs548
-rw-r--r--crypto/test/src/cms/test/SignedDataTest.cs501
-rw-r--r--crypto/test/src/crypto/io/test/AllTests.cs29
-rw-r--r--crypto/test/src/crypto/test/AeadTestUtilities.cs14
-rw-r--r--crypto/test/src/crypto/test/AllTests.cs37
-rw-r--r--crypto/test/src/crypto/test/CCMTest.cs47
-rw-r--r--crypto/test/src/crypto/test/DeterministicDSATest.cs511
-rw-r--r--crypto/test/src/crypto/test/GCMTest.cs1092
-rw-r--r--crypto/test/src/crypto/test/GMacTest.cs343
-rw-r--r--crypto/test/src/crypto/test/KeccakDigestTest.cs374
-rw-r--r--crypto/test/src/crypto/test/OAEPTest.cs1556
-rw-r--r--crypto/test/src/crypto/test/OCBTest.cs220
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs6
-rw-r--r--crypto/test/src/crypto/test/SHA3DigestTest.cs468
-rw-r--r--crypto/test/src/crypto/test/SRP6Test.cs49
-rw-r--r--crypto/test/src/crypto/test/ShakeDigestTest.cs290
-rw-r--r--crypto/test/src/crypto/test/X931SignerTest.cs145
-rw-r--r--crypto/test/src/crypto/tls/test/ByteQueueStreamTest.cs134
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs102
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestCase.cs153
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestSuite.cs134
-rw-r--r--crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs86
-rw-r--r--crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs110
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsClient.cs150
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsServer.cs100
-rw-r--r--crypto/test/src/crypto/tls/test/MockPskTlsClient.cs132
-rw-r--r--crypto/test/src/crypto/tls/test/MockPskTlsServer.cs105
-rw-r--r--crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs120
-rw-r--r--crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs113
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsClient.cs140
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsServer.cs104
-rw-r--r--crypto/test/src/crypto/tls/test/NetworkStream.cs101
-rw-r--r--crypto/test/src/crypto/tls/test/PipedStream.cs134
-rw-r--r--crypto/test/src/crypto/tls/test/PskTlsClientTest.cs79
-rw-r--r--crypto/test/src/crypto/tls/test/TlsClientTest.cs66
-rw-r--r--crypto/test/src/crypto/tls/test/TlsProtocolNonBlockingTest.cs126
-rw-r--r--crypto/test/src/crypto/tls/test/TlsProtocolTest.cs80
-rw-r--r--crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs80
-rw-r--r--crypto/test/src/crypto/tls/test/TlsServerTest.cs78
-rw-r--r--crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs80
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestCase.cs164
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs262
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestConfig.cs101
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs194
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestSuite.cs119
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestUtilities.cs167
-rw-r--r--crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs84
-rw-r--r--crypto/test/src/math/ec/test/AllTests.cs22
-rw-r--r--crypto/test/src/math/ec/test/ECAlgorithmsTest.cs151
-rw-r--r--crypto/test/src/math/ec/test/ECPointPerformanceTest.cs8
-rw-r--r--crypto/test/src/math/ec/test/ECPointTest.cs90
-rw-r--r--crypto/test/src/math/ec/test/TnafTest.cs2
-rw-r--r--crypto/test/src/math/test/AllTests.cs21
-rw-r--r--crypto/test/src/ocsp/test/AllTests.cs29
-rw-r--r--crypto/test/src/openpgp/examples/ClearSignedFileProcessor.cs28
-rw-r--r--crypto/test/src/openpgp/examples/PublicKeyRingDump.cs4
-rw-r--r--crypto/test/src/openpgp/examples/test/AllTests.cs133
-rw-r--r--crypto/test/src/openpgp/test/PGPSignatureTest.cs1698
-rw-r--r--crypto/test/src/openpgp/test/PgpECDHTest.cs280
-rw-r--r--crypto/test/src/openpgp/test/PgpECDsaTest.cs205
-rw-r--r--crypto/test/src/openpgp/test/PgpECMessageTest.cs196
-rw-r--r--crypto/test/src/openpgp/test/PgpKeyRingTest.cs34
-rw-r--r--crypto/test/src/openpgp/test/PgpParsingTest.cs40
-rw-r--r--crypto/test/src/openpgp/test/PgpUnicodeTest.cs137
-rw-r--r--crypto/test/src/openpgp/test/RegressionTest.cs4
-rw-r--r--crypto/test/src/openssl/test/AllTests.cs37
-rw-r--r--crypto/test/src/security/test/SecureRandomTest.cs291
-rw-r--r--crypto/test/src/test/CertPathValidatorTest.cs2
-rw-r--r--crypto/test/src/tsp/test/AllTests.cs27
-rw-r--r--crypto/test/src/util/io/pem/test/AllTests.cs35
-rw-r--r--crypto/test/src/util/test/UncloseableStream.cs2
-rw-r--r--crypto/test/src/x509/test/TestCertificateGen.cs32
591 files changed, 76077 insertions, 16178 deletions
diff --git a/.gitattributes b/.gitattributes
index ba5445104..4989b01a5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -14,6 +14,7 @@
 *.pem      text
 *.README   text
 *.sln      text
+*.tmpl     text
 *.txt      text
 *.xml      text diff=xml
 
@@ -23,6 +24,7 @@
 *.crl      binary
 *.crt      binary
 *.data     binary
+*.dll      binary
 *.dsa      binary
 *.jpg      binary
 *.jpeg     binary
diff --git a/.gitignore b/.gitignore
index 328e6c47f..b804bc4a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,12 @@
-**/bin
-**/dist
-**/obj
+*.psess
+*.snk
+*.suo
+*.swp
+*.user
+*.vsp
+
+Backup/
+bin/
+dist/
+doc/
+obj/
diff --git a/crypto/Contributors.html b/crypto/Contributors.html
index 68ca699cf..c62928932 100644
--- a/crypto/Contributors.html
+++ b/crypto/Contributors.html
@@ -6,7 +6,15 @@
 	</head>
 	<body>
 		<h2>The Bouncy Castle Cryptographic C#&reg; API</h2>
-		<h3>Contributors:</h3>
+		<p>
+		<h3>Donors</h3>
+		<p>
+		The following people and organisations donated financially to help with the release of 1.8:
+		<br />&nbsp;<br />
+		Andrew Grosser, Antonio Royo, dmitry.ribakov&#64gmail.com, PhreePhly, and encryptomatic.com.
+		</p>
+
+		<h3>Code Contributors:</h3>
 		<p>The following people have contributed to the C# Bouncy Castle Cryptography 
 			Package.</p>
 		<p>Thanks, may your castles never deflate!</p>
diff --git a/crypto/License.html b/crypto/License.html
index 1c5c7b0ec..cd92d1b0e 100644
--- a/crypto/License.html
+++ b/crypto/License.html
@@ -9,7 +9,7 @@
 <h2>The Bouncy Castle Cryptographic C#&reg; API</h2>
 <h3>License:</h3>
 The Bouncy Castle License<br>
-Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc.
+Copyright (c) 2000-2015 The Legion of the Bouncy Castle Inc.
 (http://www.bouncycastle.org)<br>
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"), to deal in the
diff --git a/crypto/NBuild.build b/crypto/NBuild.build
index baea45eac..64ea07545 100644
--- a/crypto/NBuild.build
+++ b/crypto/NBuild.build
@@ -16,7 +16,7 @@
   <property name="dist-path" value="./dist"/>
 
   <!-- Version -->
-  <property name="version" value="1.8.0-beta.4"/>
+  <property name="version" value="1.8.0-RC.1"/>
   <property name="name" value="BouncyCastle.Crypto"/>
 
   <property name="OPTIONAL_STRONG_NAME" value="" />
@@ -170,6 +170,7 @@
       <resources prefix="crypto" dynamicprefix="true">
         <include name="${test-datapath}/**/*.*"/>
         <exclude name="${test-datapath}/**/README.txt"/>
+        <exclude name="${test-datapath}/tls/*.tmpl"/>
       </resources>
       <references>
         <include name="mscorlib.dll"/>
diff --git a/crypto/Readme.html b/crypto/Readme.html
index 2660d4ac3..22a74800a 100644
--- a/crypto/Readme.html
+++ b/crypto/Readme.html
@@ -66,6 +66,19 @@
 		files.<br>
 		&nbsp;<br>
 		<hr style="WIDTH: 100%; HEIGHT: 2px">
+		<h3><a class="mozTocH3" name="mozTocId66345"></a>Patents:</h3>
+<p>
+ Some of the algorithms in the Bouncy Castle APIs are patented in some places. It is upon the user of the library to be aware of what the legal situation is in their own situation, however we have been asked to specifically mention the patents below, in the following terms, at the request of the patent holder.
+</p><p>
+The BC distribution contains implementations of EC MQV as described in RFC 5753, "Use of ECC Algorithms in CMS". In line with the conditions in:
+</p><p>
+<a href="http://www.ietf.org/ietf-ftp/IPR/certicom-ipr-rfc-5753.pdf">http://www.ietf.org/ietf-ftp/IPR/certicom-ipr-rfc-5753.pdf</a>
+</p><p>
+We state, where EC MQV has not otherwise been disabled or removed:
+"The use of this product or service is subject to the reasonable, non-discriminatory terms in the Intellectual Property Rights (IPR) Disclosures of Certicom Corp. at the IETF for Use of Elliptic Curve Cryptography (ECC) Algorithms in Cryptographic Message Syntax (CMS) implemented in the product or service." 
+		</p>
+		&nbsp;<br>
+		<hr style="WIDTH: 100%; HEIGHT: 2px">
 		<br>
 		<h3><a class="mozTocH3" name="mozTocId575388"></a>Features:</h3>
 		<ul>
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 5a9d37797..df7df9f5a 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -539,6 +539,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\anssi\ANSSINamedCurves.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\asn1\anssi\ANSSIObjectIdentifiers.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\bc\BCObjectIdentifiers.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -2314,6 +2324,26 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\bcpg\ECDHPublicBCPGKey.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\bcpg\ECDsaPublicBCPGKey.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\bcpg\ECPublicBCPGKey.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\bcpg\ECSecretBCPGKey.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\bcpg\ElGamalPublicBCPGKey.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -2514,6 +2544,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\bcpg\sig\Features.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\bcpg\sig\IssuerKeyID.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -2964,6 +2999,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\Check.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\CipherKeyGenerator.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -2999,6 +3039,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\IBlockResult.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\IBufferedCipher.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3039,6 +3084,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\ISignatureCalculator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\ISignatureVerifier.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\ISignatureVerifierProvider.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\ISigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3049,16 +3109,31 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\IStreamCalculator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\IStreamCipher.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\IVerifier.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\IWrapper.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\IXof.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\KeyGenerationParameters.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3069,6 +3144,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\OutputLengthException.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\PBEParametersGenerator.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3089,6 +3169,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\agreement\DHStandardGroups.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\agreement\ECDHBasicAgreement.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3139,6 +3224,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\agreement\srp\SRP6StandardGroups.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\agreement\srp\SRP6Utilities.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3159,6 +3249,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\digests\KeccakDigest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\digests\LongDigest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3239,6 +3334,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\digests\ShakeDigest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\digests\ShortenedDigest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3844,6 +3944,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\operators\Asn1Signature.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\paddings\BlockCipherPadding.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4159,6 +4264,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\parameters\Srp6GroupParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\parameters\TweakableBlockCipherParameters.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4234,6 +4344,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\HMacDsaKCalculator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\signers\IDsaKCalculator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\signers\ISO9796d2PSSSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4244,16 +4364,86 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\IsoTrailers.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\signers\PSSSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\RandomDsaKCalculator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\signers\RSADigestSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\X931Signer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsAgreementCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsCipherFactory.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsContext.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsEncryptionCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsKeyExchange.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsPeer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsSigner.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsSignerCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AlertDescription.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4264,12 +4454,12 @@
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\AlwaysValidVerifyer.cs"
+                    RelPath = "src\crypto\tls\BulkCipherAlgorithm.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\BulkCipherAlgorithm.cs"
+                    RelPath = "src\crypto\tls\BasicTlsPskIdentity.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
@@ -4279,6 +4469,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\ByteQueueStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\CertChainType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4289,6 +4484,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\CertificateStatus.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\CertificateStatusRequest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\CertificateStatusType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4299,6 +4504,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\CertificateUrl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\Chacha20Poly1305.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\ChangeCipherSpec.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4344,6 +4559,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\DatagramTransport.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\DefaultTlsAgreementCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4359,12 +4579,27 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\DefaultTlsEncryptionCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DefaultTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DefaultTlsSrpGroupVerifier.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\DefaultTlsSignerCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\DigestAlgorithm.cs"
+                    RelPath = "src\crypto\tls\DeferredHash.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
@@ -4374,6 +4609,61 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\DigitallySigned.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsClientProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsEpoch.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsHandshakeRetransmit.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsReassembler.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsRecordLayer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsReliableHandshake.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsReplayWindow.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsServerProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\DtlsTransport.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\ECBasisType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4404,42 +4694,42 @@
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\HandshakeType.cs"
+                    RelPath = "src\crypto\tls\FiniteFieldDheGroup.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\HashAlgorithm.cs"
+                    RelPath = "src\crypto\tls\HandshakeType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\HeartbeatMessageType.cs"
+                    RelPath = "src\crypto\tls\HashAlgorithm.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\HeartbeatMode.cs"
+                    RelPath = "src\crypto\tls\HeartbeatExtension.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\ICertificateVerifyer.cs"
+                    RelPath = "src\crypto\tls\HeartbeatMessage.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\KeyExchangeAlgorithm.cs"
+                    RelPath = "src\crypto\tls\HeartbeatMessageType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\LegacyTlsAuthentication.cs"
+                    RelPath = "src\crypto\tls\HeartbeatMode.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\tls\LegacyTlsClient.cs"
+                    RelPath = "src\crypto\tls\KeyExchangeAlgorithm.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
@@ -4464,6 +4754,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\NewSessionTicket.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\OcspStatusRequest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\PrfAlgorithm.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4479,6 +4779,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\PskTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\RecordStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4489,6 +4794,36 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\ServerDHParams.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\ServerSrpParams.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\ServerName.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\ServerNameList.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\ServerOnlyTlsAuthentication.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\SessionParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SignatureAlgorithm.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4504,11 +4839,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\SimulatedTlsSrpIdentityManager.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SrpTlsClient.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\SrpTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SrtpProtectionProfile.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4519,11 +4864,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\SupplementalDataEntry.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SupplementalDataType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsAeadCipher.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsAgreementCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4564,11 +4919,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsClientProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsCompression.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsContext.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4604,6 +4969,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsEccUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsECDheKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4619,11 +4989,26 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsEncryptionCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsExtensionsUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsFatalAlert.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsHandshakeHash.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4649,6 +5034,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsProtocolHandler.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4664,6 +5054,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsPskIdentityManager.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsRsaKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4679,6 +5074,36 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsServerContext.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsServerContextImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsServerProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSession.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSessionImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4689,11 +5114,36 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsSrpGroupVerifier.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSrpIdentityManager.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSrpLoginParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsSrpKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsSrpUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSrtpUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4709,57 +5159,62 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\UrlAndHash.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\UserMappingType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\crypto\util\Pack.cs"
+                    RelPath = "src\crypto\tls\UseSrtpData.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\BigInteger.cs"
+                    RelPath = "src\crypto\util\Pack.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\ECAlgorithms.cs"
+                    RelPath = "src\math\BigInteger.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\ECCurve.cs"
+                    RelPath = "src\math\Primes.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\ECFieldElement.cs"
+                    RelPath = "src\math\ec\ECAlgorithms.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\ECPoint.cs"
+                    RelPath = "src\math\ec\ECCurve.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\ECPointMap.cs"
+                    RelPath = "src\math\ec\ECFieldElement.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\LongArray.cs"
+                    RelPath = "src\math\ec\ECPoint.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\Mod.cs"
+                    RelPath = "src\math\ec\ECPointMap.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\Nat.cs"
+                    RelPath = "src\math\ec\LongArray.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
@@ -4809,27 +5264,72 @@
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\custom\sec\Nat192.cs"
+                    RelPath = "src\math\ec\custom\sec\SecP128R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP128R1Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP128R1FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP128R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP160k1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP160K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP160R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP160R1Field.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\custom\sec\Nat224.cs"
+                    RelPath = "src\math\ec\custom\sec\SecP160R1FieldElement.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\custom\sec\Nat256.cs"
+                    RelPath = "src\math\ec\custom\sec\SecP160R1Point.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\custom\sec\Nat384.cs"
+                    RelPath = "src\math\ec\custom\sec\SecP160R2Curve.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
-                    RelPath = "src\math\ec\custom\sec\Nat512.cs"
+                    RelPath = "src\math\ec\custom\sec\SecP160R2Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP160R2FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecP160R2Point.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
@@ -4994,6 +5494,276 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\math\ec\custom\sec\SecT113Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT113FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT113R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT113R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT113R2Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT113R2Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT131Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT131FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT131R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT131R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT131R2Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT131R2Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163K1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163R2Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT163R2Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT193Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT193FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT193R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT193R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT193R2Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT193R2Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT233Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT233FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT233K1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT233K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT233R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT233R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT239Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT239FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT239K1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT239K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT283Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT283FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT283K1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT283K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT283R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT283R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT409Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT409FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT409K1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT409K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT409R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT409R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT571Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT571FieldElement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT571K1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT571K1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT571R1Curve.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\custom\sec\SecT571R1Point.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\math\ec\endo\ECEndomorphism.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -5154,6 +5924,71 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\math\raw\Interleave.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Mod.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat128.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat160.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat192.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat224.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat256.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat320.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat384.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat448.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat512.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\raw\Nat576.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\ocsp\BasicOCSPResp.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -5344,6 +6179,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\openpgp\PgpPad.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\openpgp\PGPPBEEncryptedData.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -5434,6 +6274,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\openpgp\Rfc6637Utilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\openpgp\SXprUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\openpgp\WrappedGeneratorStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -5879,6 +6729,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\util\Times.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\util\collections\CollectionUtilities.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -6014,6 +6869,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\util\io\FilterStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\util\io\NullOutputStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -6390,6 +7250,14 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
+                    RelPath = "test\data\crypto\SHA3TestVectors.txt"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\crypto\SHAKETestVectors.txt"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
                     RelPath = "test\data\hc256\hc128\ecrypt_HC-128.txt"
                     BuildAction = "EmbeddedResource"
                 />
@@ -6626,6 +7494,14 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
+                    RelPath = "test\data\openpgp\bigpub.asc"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\openpgp\longSigSubPack.asc"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
                     RelPath = "test\data\openpgp\dsa\README.txt"
                     BuildAction = "None"
                 />
@@ -6706,6 +7582,22 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
+                    RelPath = "test\data\openpgp\unicode\passphrase_cyr.txt"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\openpgp\unicode\passphrase_for_test.txt"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\openpgp\unicode\secring.gpg"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\openpgp\unicode\test.asc"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
                     RelPath = "test\data\openssl\eckey.pem"
                     BuildAction = "EmbeddedResource"
                 />
@@ -9594,6 +10486,62 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
+                    RelPath = "test\data\tls\x509-ca.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-ca-key.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-ecdsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-key.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-key-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-key-ecdsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-ecdsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-key.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-key-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-key-ecdsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
                     RelPath = "test\data\tls\keystores\client_store.dsa"
                     BuildAction = "EmbeddedResource"
                 />
@@ -10057,6 +11005,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\AeadTestUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\AESFastTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10152,6 +11105,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\DeterministicDSATest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\DHKEKGeneratorTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10297,6 +11255,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\KeccakDigestTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\MacTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10547,6 +11510,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\ShakeDigestTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\ShortenedDigestTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10637,6 +11605,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\X931SignerTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\XSalsa20Test.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10647,6 +11620,156 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\ByteQueueStreamTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestCase.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestSuite.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\LoggingDatagramTransport.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDatagramAssociation.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDtlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDtlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockPskTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockPskTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockSrpTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockSrpTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\NetworkStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\PipedStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\PskTlsClientTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsClientTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsProtocolNonBlockingTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsPskProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsSrpProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsServerTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestCase.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestClientImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestConfig.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestServerImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestSuite.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\UnreliableDatagramTransport.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\math\ec\test\AllTests.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10657,6 +11780,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\math\ec\test\ECAlgorithmsTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\math\ec\test\ECPointTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10792,6 +11920,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\openpgp\test\PgpECDHTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\openpgp\test\PgpECDsaTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\openpgp\test\PgpECMessageTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\openpgp\test\PgpKeyRingTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10812,6 +11955,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\openpgp\test\PgpParsingTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\openpgp\test\PGPPBETest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10827,6 +11975,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\openpgp\test\PgpUnicodeTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\openpgp\test\RegressionTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
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 Lenstras 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>
diff --git a/crypto/test/data/crypto/SHA3TestVectors.txt b/crypto/test/data/crypto/SHA3TestVectors.txt
new file mode 100644
index 000000000..c6d0efafc
--- /dev/null
+++ b/crypto/test/data/crypto/SHA3TestVectors.txt
@@ -0,0 +1,837 @@
+# Test vectors for FIPS 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
+#
+# Downloaded 6th August, 2015 from http://csrc.nist.gov/groups/ST/toolkit/examples.html#aHashing
+#
+# NOTE: The 1605-bit test vectors were initially wrong. Corrections were published on 14th August, 2015
+#       (SHA3-512 corrected on 3rd September, 2015).
+
+SHA3-224 sample of 0-bit message
+
+Msg as bit string
+    #(empty message)
+
+Hash val is
+    6B 4E 03 42 36 67 DB B7 3B 6E 15 45 4F 0E B1 AB
+    D4 59 7F 9A 1B 07 8E 3F 5B 5A 6B C7
+
+SHA3-224 sample of 5-bit message
+
+Msg as bit string
+    1 1 0 0 1
+
+Hash val is
+    FF BA D5 DA 96 BA D7 17 89 33 02 06 DC 67 68 EC
+    AE B1 B3 2D CA 6B 33 01 48 96 74 AB
+
+SHA3-224 sample of 30-bit message
+
+Msg as bit string
+    1 1 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0
+
+Hash val is
+    D6 66 A5 14 CC 9D BA 25 AC 1B A6 9E D3 93 04 60
+    DE AA C9 85 1B 5F 0B AA B0 07 DF 3B
+
+SHA3-224 sample of 1600-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+
+Hash val is
+    93 76 81 6A BA 50 3F 72 F9 6C E7 EB 65 AC 09 5D
+    EE E3 BE 4B F9 BB C2 A1 CB 7E 11 E0
+
+SHA3-224 sample of 1605-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0
+
+Hash val is
+    22 D2 F7 BB 0B 17 3F D8 C1 96 86 F9 17 31 66 E3
+    EE 62 73 80 47 D7 EA DD 69 EF B2 28
+
+SHA3-224 sample of 1630-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1
+
+Hash val is
+    4E 90 7B B1 05 78 61 F2 00 A5 99 E9 D4 F8 5B 02
+    D8 84 53 BF 5B 8A CE 9A C5 89 13 4C
+
+SHA3-256 sample of 0-bit message
+
+Msg as bit string
+    #(empty message)
+
+Hash val is
+    A7 FF C6 F8 BF 1E D7 66 51 C1 47 56 A0 61 D6 62
+    F5 80 FF 4D E4 3B 49 FA 82 D8 0A 4B 80 F8 43 4A
+
+SHA3-256 sample of 5-bit message
+
+Msg as bit string
+    1 1 0 0 1
+
+Hash val is
+    7B 00 47 CF 5A 45 68 82 36 3C BF 0F B0 53 22 CF
+    65 F4 B7 05 9A 46 36 5E 83 01 32 E3 B5 D9 57 AF
+
+SHA3-256 sample of 30-bit message
+
+Msg as bit string
+    1 1 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0
+
+Hash val is
+    C8 24 2F EF 40 9E 5A E9 D1 F1 C8 57 AE 4D C6 24
+    B9 2B 19 80 9F 62 AA 8C 07 41 1C 54 A0 78 B1 D0
+
+SHA3-256 sample of 1600-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+
+Hash val is
+    79 F3 8A DE C5 C2 03 07 A9 8E F7 6E 83 24 AF BF
+    D4 6C FD 81 B2 2E 39 73 C6 5F A1 BD 9D E3 17 87
+
+SHA3-256 sample of 1605-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0
+
+Hash val is
+    81 EE 76 9B ED 09 50 86 2B 1D DD ED 2E 84 AA A6
+    AB 7B FD D3 CE AA 47 1B E3 11 63 D4 03 36 36 3C
+
+SHA3-256 sample of 1630-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1
+
+Hash val is
+    52 86 0A A3 01 21 4C 61 0D 92 2A 6B 6C AB 98 1C
+    CD 06 01 2E 54 EF 68 9D 74 40 21 E7 38 B9 ED 20
+
+SHA3-384 sample of 0-bit message
+
+Msg as bit string
+    #(empty message)
+
+Hash val is
+    0C 63 A7 5B 84 5E 4F 7D 01 10 7D 85 2E 4C 24 85
+    C5 1A 50 AA AA 94 FC 61 99 5E 71 BB EE 98 3A 2A
+    C3 71 38 31 26 4A DB 47 FB 6B D1 E0 58 D5 F0 04
+
+SHA3-384 sample of 5-bit message
+
+Msg as bit string
+    1 1 0 0 1
+
+Hash val is
+    73 7C 9B 49 18 85 E9 BF 74 28 E7 92 74 1A 7B F8
+    DC A9 65 34 71 C3 E1 48 47 3F 2C 23 6B 6A 0A 64
+    55 EB 1D CE 9F 77 9B 4B 6B 23 7F EF 17 1B 1C 64
+
+SHA3-384 sample of 30-bit message
+
+Msg as bit string
+    1 1 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0
+
+Hash val is
+    95 5B 4D D1 BE 03 26 1B D7 6F 80 7A 7E FD 43 24
+    35 C4 17 36 28 11 B8 A5 0C 56 4E 7E E9 58 5E 1A
+    C7 62 6D DE 2F DC 03 0F 87 61 96 EA 26 7F 08 C3
+
+SHA3-384 sample of 1600-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+
+Hash val is
+    18 81 DE 2C A7 E4 1E F9 5D C4 73 2B 8F 5F 00 2B
+    18 9C C1 E4 2B 74 16 8E D1 73 26 49 CE 1D BC DD
+    76 19 7A 31 FD 55 EE 98 9F 2D 70 50 DD 47 3E 8F
+
+SHA3-384 sample of 1605-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0
+
+Hash val is
+    A3 1F DB D8 D5 76 55 1C 21 FB 11 91 B5 4B DA 65
+    B6 C5 FE 97 F0 F4 A6 91 03 42 4B 43 F7 FD B8 35
+    97 9F DB EA E8 B3 FE 16 CB 82 E5 87 38 1E B6 24
+
+SHA3-384 sample of 1630-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1
+
+Hash val is
+    34 85 D3 B2 80 BD 38 4C F4 A7 77 84 4E 94 67 81
+    73 05 5D 1C BC 40 C7 C2 C3 83 3D 9E F1 23 45 17
+    2D 6F CD 31 92 3B B8 79 5A C8 18 47 D3 D8 85 5C
+
+SHA3-512 sample of 0-bit message
+
+Msg as bit string
+    #(empty message)
+
+Hash val is
+    A6 9F 73 CC A2 3A 9A C5 C8 B5 67 DC 18 5A 75 6E
+    97 C9 82 16 4F E2 58 59 E0 D1 DC C1 47 5C 80 A6
+    15 B2 12 3A F1 F5 F9 4C 11 E3 E9 40 2C 3A C5 58
+    F5 00 19 9D 95 B6 D3 E3 01 75 85 86 28 1D CD 26
+
+SHA3-512 sample of 5-bit message
+
+Msg as bit string
+    1 1 0 0 1
+
+Hash val is
+    A1 3E 01 49 41 14 C0 98 00 62 2A 70 28 8C 43 21
+    21 CE 70 03 9D 75 3C AD D2 E0 06 E4 D9 61 CB 27
+    54 4C 14 81 E5 81 4B DC EB 53 BE 67 33 D5 E0 99
+    79 5E 5E 81 91 8A DD B0 58 E2 2A 9F 24 88 3F 37
+
+SHA3-512 sample of 30-bit message
+
+Msg as bit string
+    1 1 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0
+
+Hash val is
+    98 34 C0 5A 11 E1 C5 D3 DA 9C 74 0E 1C 10 6D 9E
+    59 0A 0E 53 0B 6F 6A AA 78 30 52 5D 07 5C A5 DB
+    1B D8 A6 AA 98 1A 28 61 3A C3 34 93 4A 01 82 3C
+    D4 5F 45 E4 9B 6D 7E 69 17 F2 F1 67 78 06 7B AB
+
+SHA3-512 sample of 1600-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+
+Hash val is
+    E7 6D FA D2 20 84 A8 B1 46 7F CF 2F FA 58 36 1B
+    EC 76 28 ED F5 F3 FD C0 E4 80 5D C4 8C AE EC A8
+    1B 7C 13 C3 0A DF 52 A3 65 95 84 73 9A 2D F4 6B
+    E5 89 C5 1C A1 A4 A8 41 6D F6 54 5A 1C E8 BA 00
+
+SHA3-512 sample of 1605-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0
+
+Hash val is
+    FC 4A 16 7C CB 31 A9 37 D6 98 FD E8 2B 04 34 8C
+    95 39 B2 8F 0C 9D 3B 45 05 70 9C 03 81 23 50 E4
+    99 0E 96 22 97 4F 6E 57 5C 47 86 1C 0D 2E 63 8C
+    CF C2 02 3C 36 5B B6 0A 93 F5 28 55 06 98 78 6B
+
+SHA3-512 sample of 1630-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1
+
+Hash val is
+    CF 9A 30 AC 1F 1F 6A C0 91 6F 9F EF 19 19 C5 95
+    DE BE 2E E8 0C 85 42 12 10 FD F0 5F 1C 6A F7 3A
+    A9 CA C8 81 D0 F9 1D B6 D0 34 A2 BB AD C1 CF 7F
+    BC B2 EC FA 9D 19 1D 3A 50 16 FB 3F AD 87 09 C9
+
diff --git a/crypto/test/data/crypto/SHAKETestVectors.txt b/crypto/test/data/crypto/SHAKETestVectors.txt
new file mode 100644
index 000000000..b5c9be3cd
--- /dev/null
+++ b/crypto/test/data/crypto/SHAKETestVectors.txt
@@ -0,0 +1,770 @@
+# Test vectors for FIPS 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
+#
+# Downloaded 6th August, 2015 from http://csrc.nist.gov/groups/ST/toolkit/examples.html#aHashing
+
+SHAKE-128 sample of 0-bit message
+
+Msg as bit string
+    #(empty message)
+
+Output val is
+    7F 9C 2B A4 E8 8F 82 7D 61 60 45 50 76 05 85 3E
+    D7 3B 80 93 F6 EF BC 88 EB 1A 6E AC FA 66 EF 26
+    3C B1 EE A9 88 00 4B 93 10 3C FB 0A EE FD 2A 68
+    6E 01 FA 4A 58 E8 A3 63 9C A8 A1 E3 F9 AE 57 E2
+    35 B8 CC 87 3C 23 DC 62 B8 D2 60 16 9A FA 2F 75
+    AB 91 6A 58 D9 74 91 88 35 D2 5E 6A 43 50 85 B2
+    BA DF D6 DF AA C3 59 A5 EF BB 7B CC 4B 59 D5 38
+    DF 9A 04 30 2E 10 C8 BC 1C BF 1A 0B 3A 51 20 EA
+    17 CD A7 CF AD 76 5F 56 23 47 4D 36 8C CC A8 AF
+    00 07 CD 9F 5E 4C 84 9F 16 7A 58 0B 14 AA BD EF
+    AE E7 EE F4 7C B0 FC A9 76 7B E1 FD A6 94 19 DF
+    B9 27 E9 DF 07 34 8B 19 66 91 AB AE B5 80 B3 2D
+    EF 58 53 8B 8D 23 F8 77 32 EA 63 B0 2B 4F A0 F4
+    87 33 60 E2 84 19 28 CD 60 DD 4C EE 8C C0 D4 C9
+    22 A9 61 88 D0 32 67 5C 8A C8 50 93 3C 7A FF 15
+    33 B9 4C 83 4A DB B6 9C 61 15 BA D4 69 2D 86 19
+    F9 0B 0C DF 8A 7B 9C 26 40 29 AC 18 5B 70 B8 3F
+    28 01 F2 F4 B3 F7 0C 59 3E A3 AE EB 61 3A 7F 1B
+    1D E3 3F D7 50 81 F5 92 30 5F 2E 45 26 ED C0 96
+    31 B1 09 58 F4 64 D8 89 F3 1B A0 10 25 0F DA 7F
+    13 68 EC 29 67 FC 84 EF 2A E9 AF F2 68 E0 B1 70
+    0A FF C6 82 0B 52 3A 3D 91 71 35 F2 DF F2 EE 06
+    BF E7 2B 31 24 72 1D 4A 26 C0 4E 53 A7 5E 30 E7
+    3A 7A 9C 4A 95 D9 1C 55 D4 95 E9 F5 1D D0 B5 E9
+    D8 3C 6D 5E 8C E8 03 AA 62 B8 D6 54 DB 53 D0 9B
+    8D CF F2 73 CD FE B5 73 FA D8 BC D4 55 78 BE C2
+    E7 70 D0 1E FD E8 6E 72 1A 3F 7C 6C CE 27 5D AB
+    E6 E2 14 3F 1A F1 8D A7 EF DD C4 C7 B7 0B 5E 34
+    5D B9 3C C9 36 BE A3 23 49 1C CB 38 A3 88 F5 46
+    A9 FF 00 DD 4E 13 00 B9 B2 15 3D 20 41 D2 05 B4
+    43 E4 1B 45 A6 53 F2 A5 C4 49 2C 1A DD 54 45 12
+    DD A2 52 98 33 46 2B 71 A4 1A 45 BE 97 29 0B 6F
+
+SHAKE-128 sample of 5-bit message
+
+Msg as bit string
+    1 1 0 0 1
+
+Output val is
+    2E 0A BF BA 83 E6 72 0B FB C2 25 FF 6B 7A B9 FF
+    CE 58 BA 02 7E E3 D8 98 76 4F EF 28 7D DE CC CA
+    3E 6E 59 98 41 1E 7D DB 32 F6 75 38 F5 00 B1 8C
+    8C 97 C4 52 C3 70 EA 2C F0 AF CA 3E 05 DE 7E 4D
+    E2 7F A4 41 A9 CB 34 FD 17 C9 78 B4 2D 5B 7E 7F
+    9A B1 8F FE FF C3 C5 AC 2F 3A 45 5E EB FD C7 6C
+    EA EB 0A 2C CA 22 EE F6 E6 37 F4 CA BE 5C 51 DE
+    D2 E3 FA D8 B9 52 70 A3 21 84 56 64 F1 07 D1 64
+    96 BB 7A BF BE 75 04 B6 ED E2 E8 9E 4B 99 6F B5
+    8E FD C4 18 1F 91 63 38 1C BE 7B C0 06 A7 A2 05
+    98 9C 52 6C D1 BD 68 98 36 93 B4 BD C5 37 28 B2
+    41 C1 CF F4 2B B6 11 50 2C 35 20 5C AB B2 88 75
+    56 55 D6 20 C6 79 94 F0 64 51 18 7F 6F D1 7E 04
+    66 82 BA 12 86 06 3F F8 8F E2 50 8D 1F CA F9 03
+    5A 12 31 AD 41 50 A9 C9 B2 4C 9B 2D 66 B2 AD 1B
+    DE 0B D0 BB CB 8B E0 5B 83 52 29 EF 79 19 73 73
+    23 42 44 01 E1 D8 37 B6 6E B4 E6 30 FF 1D E7 0C
+    B3 17 C2 BA CB 08 00 1D 34 77 B7 A7 0A 57 6D 20
+    86 90 33 58 9D 85 A0 1D DB 2B 66 46 C0 43 B5 9F
+    C0 11 31 1D A6 66 FA 5A D1 D6 38 7F A9 BC 40 15
+    A3 8A 51 D1 DA 1E A6 1D 64 8D C8 E3 9A 88 B9 D6
+    22 BD E2 07 FD AB C6 F2 82 7A 88 0C 33 0B BF 6D
+    F7 33 77 4B 65 3E 57 30 5D 78 DC E1 12 F1 0A 2C
+    71 F4 CD AD 92 ED 11 3E 1C EA 63 B9 19 25 ED 28
+    19 1E 6D BB B5 AA 5A 2A FD A5 1F C0 5A 3A F5 25
+    8B 87 66 52 43 55 0F 28 94 8A E2 B8 BE B6 BC 9C
+    77 0B 35 F0 67 EA A6 41 EF E6 5B 1A 44 90 9D 1B
+    14 9F 97 EE A6 01 39 1C 60 9E C8 1D 19 30 F5 7C
+    18 A4 E0 FA B4 91 D1 CA DF D5 04 83 44 9E DC 0F
+    07 FF B2 4D 2C 6F 9A 9A 3B FF 39 AE 3D 57 F5 60
+    65 4D 7D 75 C9 08 AB E6 25 64 75 3E AC 39 D7 50
+    3D A6 D3 7C 2E 32 E1 AF 3B 8A EC 8A E3 06 9C D9
+
+SHAKE-128 sample of 30-bit message
+
+Msg as bit string
+    1 1 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0
+
+Output val is
+    6D 5D 39 C5 5F 3C CA 56 7F EA F4 22 DC 64 BA 17
+    40 1D 07 75 6D 78 B0 FA 3D 54 6D 66 AF C2 76 71
+    E0 01 06 85 FC 69 A7 EC 3C 53 67 B8 FA 5F DA 39
+    D5 7C E5 3F 15 3F A4 03 1D 27 72 06 77 0A EC 6B
+    2D DF 16 AE FA B6 69 11 0D 6E 4A 29 6A 14 FB 14
+    86 B0 84 6B 69 05 43 E4 05 7F 7F 42 AA 8C 0E 6A
+    5A 56 B6 0B 68 8D 55 A1 96 DF 6F 39 76 E3 06 88
+    CB B6 AF D4 85 25 D7 64 90 35 7F 3F D8 97 BA FC
+    87 36 D9 07 B9 BA C8 16 59 1F C2 4E 79 36 0B E3
+    A7 FF A6 29 82 C4 5A BB 0E 58 4C 07 EC 93 A1 95
+    30 50 9D 9F 81 62 15 D7 27 7B B9 99 43 7C 82 14
+    50 F0 75 92 81 CD 8E 16 A3 48 3E 3C C7 52 09 1B
+    7A AE 92 90 9D 2F 50 1E F7 DC E9 89 75 98 91 B3
+    37 7C EA B4 93 FF E4 96 01 0A 0C 7E 51 95 99 94
+    F5 6F 56 5E 63 3A F6 09 3A C6 E1 E0 F0 04 88 71
+    EC 47 78 F4 8E F8 BD 5B CB 80 EA 7D F9 FF 47 11
+    C8 1E 24 C0 22 1C 2A D9 74 4F BA 79 35 EA EC A1
+    14 22 4F D1 08 EF C5 AC 74 C6 62 52 08 92 75 B4
+    27 76 73 70 8C 4A F9 2F 88 13 B1 93 59 9F D6 4B
+    D7 48 4F 2E 5E C3 69 E3 64 64 99 76 8E 58 1D D0
+    53 AA 48 14 D8 BF 1A CF F5 FD 77 45 19 A7 49 BE
+    66 75 47 41 EB C5 36 22 12 A9 FE A8 A8 14 E9 E0
+    10 BC 27 20 B3 B7 D9 4F AB 74 BC 7F 92 3E 10 72
+    B8 A5 DD DD A8 3B A0 15 7D 8C BA 55 C1 92 DF 69
+    65 CB 7D BA 46 A3 34 0D F8 C3 FA 89 C7 C4 DB 53
+    9D 38 DC 40 6F 1D 2C F5 4E 59 05 58 0B 44 04 BF
+    D7 B3 71 95 61 C5 A5 9D 5D FD B1 BF 93 DF 13 82
+    52 25 ED CC E0 FA 7D 87 EF CD 23 9F EB 49 FC 9E
+    2D E9 D8 28 FE EB 1F 2C F5 79 B9 5D D0 50 AB 2C
+    A4 71 05 A8 D3 0F 3F D2 A1 15 4C 15 F8 7F B3 7B
+    2C 71 56 BD 7F 3C F2 B7 45 C9 12 A4 0B C1 B5 59
+    B6 56 E3 E9 03 CC 57 33 E8 6B A1 5D FE F7 06 78
+
+SHAKE-128 sample of 1600-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+
+Output val is
+    13 1A B8 D2 B5 94 94 6B 9C 81 33 3F 9B B6 E0 CE
+    75 C3 B9 31 04 FA 34 69 D3 91 74 57 38 5D A0 37
+    CF 23 2E F7 16 4A 6D 1E B4 48 C8 90 81 86 AD 85
+    2D 3F 85 A5 CF 28 DA 1A B6 FE 34 38 17 19 78 46
+    7F 1C 05 D5 8C 7E F3 8C 28 4C 41 F6 C2 22 1A 76
+    F1 2A B1 C0 40 82 66 02 50 80 22 94 FB 87 18 02
+    13 FD EF 5B 0E CB 7D F5 0C A1 F8 55 5B E1 4D 32
+    E1 0F 6E DC DE 89 2C 09 42 4B 29 F5 97 AF C2 70
+    C9 04 55 6B FC B4 7A 7D 40 77 8D 39 09 23 64 2B
+    3C BD 05 79 E6 09 08 D5 A0 00 C1 D0 8B 98 EF 93
+    3F 80 64 45 BF 87 F8 B0 09 BA 9E 94 F7 26 61 22
+    ED 7A C2 4E 5E 26 6C 42 A8 2F A1 BB EF B7 B8 DB
+    00 66 E1 6A 85 E0 49 3F 07 DF 48 09 AE C0 84 A5
+    93 74 8A C3 DD E5 A6 D7 AA E1 E8 B6 E5 35 2B 2D
+    71 EF BB 47 D4 CA EE D5 E6 D6 33 80 5D 2D 32 3E
+    6F D8 1B 46 84 B9 3A 26 77 D4 5E 74 21 C2 C6 AE
+    A2 59 B8 55 A6 98 FD 7D 13 47 7A 1F E5 3E 5A 4A
+    61 97 DB EC 5C E9 5F 50 5B 52 0B CD 95 70 C4 A8
+    26 5A 7E 01 F8 9C 0C 00 2C 59 BF EC 6C D4 A5 C1
+    09 25 89 53 EE 5E E7 0C D5 77 EE 21 7A F2 1F A7
+    01 78 F0 94 6C 9B F6 CA 87 51 79 34 79 F6 B5 37
+    73 7E 40 B6 ED 28 51 1D 8A 2D 7E 73 EB 75 F8 DA
+    AC 91 2F F9 06 E0 AB 95 5B 08 3B AC 45 A8 E5 E9
+    B7 44 C8 50 6F 37 E9 B4 E7 49 A1 84 B3 0F 43 EB
+    18 8D 85 5F 1B 70 D7 1F F3 E5 0C 53 7A C1 B0 F8
+    97 4F 0F E1 A6 AD 29 5B A4 2F 6A EC 74 D1 23 A7
+    AB ED DE 6E 2C 07 11 CA B3 6B E5 AC B1 A5 A1 1A
+    4B 1D B0 8B A6 98 2E FC CD 71 69 29 A7 74 1C FC
+    63 AA 44 35 E0 B6 9A 90 63 E8 80 79 5C 3D C5 EF
+    32 72 E1 1C 49 7A 91 AC F6 99 FE FE E2 06 22 7A
+    44 C9 FB 35 9F D5 6A C0 A9 A7 5A 74 3C FF 68 62
+    F1 7D 72 59 AB 07 52 16 C0 69 95 11 64 3B 64 39
+
+SHAKE-128 sample of 1605-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0
+
+Output val is
+    4A C3 8E BD 16 78 B4 A4 52 79 2C 56 73 F9 77 7D
+    36 B5 54 51 AA AE 24 24 92 49 42 D3 18 A2 F6 F5
+    1B BC 83 7D CC 70 22 C5 40 3B 69 D2 9A C9 9A 74
+    5F 06 D0 6F 2A 41 B0 CC 24 3C D2 70 FA 44 D4 30
+    65 AF 00 D2 AD 35 8B D5 A5 D0 6D 33 1B C2 30 CD
+    8D DA 46 55 62 8F 91 02 71 1A DA FB 76 36 C1 60
+    B2 D2 5E C6 23 5A 2F E0 F3 73 94 D8 7F C5 FF D7
+    DB F1 99 3E 55 8A EB EA 6C 61 E9 07 18 8C 61 F5
+    FC DE 27 8E 26 4F 95 8F FD 7B 33 82 DC 10 13 9B
+    62 5E 12 41 AB 5B BC 2A 1F BC AC 31 A3 35 CF C7
+    B2 0E 42 77 12 24 6C BB 55 23 22 59 A7 EF 16 02
+    BD 56 F6 56 7D 66 94 2D 4A 71 49 F4 22 22 10 B0
+    74 EA 54 15 4B 38 E8 FD FA 0D CF 4F A3 EC D2 15
+    4E 83 18 A6 57 8B 53 5D BC FC 21 7A 3C AB 52 53
+    29 65 84 6F 89 78 14 57 02 55 63 E2 DC 15 CC 3A
+    F9 02 BA 2A D2 80 FF BB BF A4 C5 2B 60 FA 41 BA
+    C2 1F 4A B2 35 36 26 81 19 FC 98 CD 98 2D A5 CD
+    5D A2 1E 1B 56 92 D4 71 05 DE 9F 1E 01 32 C6 FE
+    31 5D 67 FA 46 49 97 C2 AB 55 33 C7 9F 98 E6 E6
+    4F F8 08 02 A7 FE 96 CA 04 A8 1F 88 55 27 37 0A
+    22 06 B1 0B 39 36 DD 81 B8 24 63 53 F4 CD 90 51
+    10 89 26 8D 74 4F 21 0A C6 89 D4 9D 28 75 05 4A
+    72 7B 60 4D 13 D2 69 B3 71 90 D4 27 C7 D1 5C CC
+    DC D7 87 0E 0B 8A DB EB 97 71 11 A9 BC F7 78 1A
+    16 13 56 A5 94 1C 79 99 07 EF 9D 3B 1A 44 1F 09
+    51 5F 28 31 C4 FA FD E3 DC 7C 1E 9B 5A A5 7D 3E
+    83 CD 67 34 DA 3D 8B 9E F3 FC 44 88 05 EA 29 C9
+    9C BA 6B 35 2B CA BE 2F D9 70 AE 95 80 D2 BF 25
+    15 2B 96 0E 6B 80 6D 87 D7 D0 60 8B 24 7F 61 08
+    9E 29 86 92 C2 7F 19 C5 2D 03 EB E3 95 A3 68 06
+    AD 54 0B EC 2D 04 6C 18 E3 55 FA F8 31 3D 2E F8
+    99 5E E6 AA E4 25 68 F3 14 93 3E 3A 21 E5 BE 40
+
+SHAKE-128 sample of 1630-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1
+
+Output val is
+    89 84 6D C7 76 AC 0F 01 45 72 EA 79 F5 60 77 34
+    51 00 29 38 24 8E 68 82 56 9A C3 2A EA B1 91 FC
+    AC DE 68 EB 07 55 75 39 C4 84 5F B4 44 10 8E 6E
+    05 45 E7 31 FC CA 2D 4F 67 A3 BF D4 1C FF 3E AF
+    35 EE FB 53 44 11 77 96 5B B5 16 95 0C F5 DC B2
+    AA FC BB C6 30 0E 8E EF D9 BC D0 E5 F3 2D 1A 4E
+    87 2E 0F 1D BD 8F 8E 00 CB B8 78 69 8C 58 83 E3
+    CA 18 4B 94 90 38 9E 46 00 2C 08 A0 B1 6B 05 A3
+    6B 2C B5 A1 CA E0 8E 11 AD 97 2F D2 4A F7 01 01
+    CE 47 46 C8 4F 16 71 87 7F 0D F6 C4 15 D1 67 0F
+    F4 0B 8D DE DD 89 CC 3E 65 6D B9 05 80 49 D6 09
+    B6 78 4C C9 D0 5E 60 CC 6A C9 C8 19 49 93 BA 29
+    15 8F D4 DB 8C F2 25 E9 57 4F 18 A7 7F 66 EC 10
+    52 BF 17 99 3B DA 20 6A 17 73 7D 78 5B D4 C1 8C
+    EE 4C 76 AA 57 35 A5 22 3F 3C 55 E7 9D AE C1 3D
+    4B F6 0F 15 62 E0 AD 0F A3 B5 58 EC CF A8 AB 3E
+    EF 61 47 4D 57 6E 8C AF 4C 11 E4 DE 5C CB 36 D7
+    DF 7D 89 2C 1F CA 20 17 BE 8B BD A5 A4 71 95 44
+    8C C6 7A 07 8E 62 8A 2E F7 63 FF E1 DC 9D 9D 6F
+    F7 8E 68 96 1C 33 FF D9 00 0C 11 DE E7 F7 40 8D
+    8D A5 C6 05 B0 B4 D5 6B B5 5E 93 64 C7 7B FA D9
+    C8 19 1E D6 E1 FE 7B 7A 93 7C 6D 07 09 5F E5 EA
+    91 A7 00 B4 BD FC 17 B4 28 D0 36 92 2A A8 AB 5E
+    2C D5 85 84 6F B8 1F C6 93 B8 D5 9B F8 5C 74 BC
+    70 0C D2 BC 3E 6A AB 43 7D 93 D8 A3 0F 1C F6 92
+    EF EF 43 60 20 28 E0 CE 57 42 EB 3F 4F 4D 5B 02
+    91 58 DD 68 96 AC B5 E3 A7 F6 84 D9 AA 89 14 E7
+    09 74 B2 23 A6 FE C3 8D 76 C7 47 3E 86 E4 B9 B3
+    2C 62 1E 20 15 C5 5E 94 7D D0 16 C6 75 C8 23 68
+    CE 26 FB 45 6A 5B 65 88 1A F5 13 BF DC 88 68 7C
+    63 81 67 6A BB D2 D9 10 4E D2 3A 9E 89 31 02 46
+    B0 26 CE DD 57 59 5B 1A B6 FE 88 A7 84 BE 0C 06
+
+SHAKE-256 sample of 0-bit message
+
+Msg as bit string
+    #(empty message)
+
+Output val is
+    46 B9 DD 2B 0B A8 8D 13 23 3B 3F EB 74 3E EB 24
+    3F CD 52 EA 62 B8 1B 82 B5 0C 27 64 6E D5 76 2F
+    D7 5D C4 DD D8 C0 F2 00 CB 05 01 9D 67 B5 92 F6
+    FC 82 1C 49 47 9A B4 86 40 29 2E AC B3 B7 C4 BE
+    14 1E 96 61 6F B1 39 57 69 2C C7 ED D0 B4 5A E3
+    DC 07 22 3C 8E 92 93 7B EF 84 BC 0E AB 86 28 53
+    34 9E C7 55 46 F5 8F B7 C2 77 5C 38 46 2C 50 10
+    D8 46 C1 85 C1 51 11 E5 95 52 2A 6B CD 16 CF 86
+    F3 D1 22 10 9E 3B 1F DD 94 3B 6A EC 46 8A 2D 62
+    1A 7C 06 C6 A9 57 C6 2B 54 DA FC 3B E8 75 67 D6
+    77 23 13 95 F6 14 72 93 B6 8C EA B7 A9 E0 C5 8D
+    86 4E 8E FD E4 E1 B9 A4 6C BE 85 47 13 67 2F 5C
+    AA AE 31 4E D9 08 3D AB 4B 09 9F 8E 30 0F 01 B8
+    65 0F 1F 4B 1D 8F CF 3F 3C B5 3F B8 E9 EB 2E A2
+    03 BD C9 70 F5 0A E5 54 28 A9 1F 7F 53 AC 26 6B
+    28 41 9C 37 78 A1 5F D2 48 D3 39 ED E7 85 FB 7F
+    5A 1A AA 96 D3 13 EA CC 89 09 36 C1 73 CD CD 0F
+    AB 88 2C 45 75 5F EB 3A ED 96 D4 77 FF 96 39 0B
+    F9 A6 6D 13 68 B2 08 E2 1F 7C 10 D0 4A 3D BD 4E
+    36 06 33 E5 DB 4B 60 26 01 C1 4C EA 73 7D B3 DC
+    F7 22 63 2C C7 78 51 CB DD E2 AA F0 A3 3A 07 B3
+    73 44 5D F4 90 CC 8F C1 E4 16 0F F1 18 37 8F 11
+    F0 47 7D E0 55 A8 1A 9E DA 57 A4 A2 CF B0 C8 39
+    29 D3 10 91 2F 72 9E C6 CF A3 6C 6A C6 A7 58 37
+    14 30 45 D7 91 CC 85 EF F5 B2 19 32 F2 38 61 BC
+    F2 3A 52 B5 DA 67 EA F7 BA AE 0F 5F B1 36 9D B7
+    8F 3A C4 5F 8C 4A C5 67 1D 85 73 5C DD DB 09 D2
+    B1 E3 4A 1F C0 66 FF 4A 16 2C B2 63 D6 54 12 74
+    AE 2F CC 86 5F 61 8A BE 27 C1 24 CD 8B 07 4C CD
+    51 63 01 B9 18 75 82 4D 09 95 8F 34 1E F2 74 BD
+    AB 0B AE 31 63 39 89 43 04 E3 58 77 B0 C2 8A 9B
+    1F D1 66 C7 96 B9 CC 25 8A 06 4A 8F 57 E2 7F 2A
+
+SHAKE-256 sample of 5-bit message
+
+Msg as bit string
+    1 1 0 0 1
+
+Output val is
+    48 A5 C1 1A BA EE FF 09 2F 36 46 EF 0D 6B 3D 3F
+    F7 6C 2F 55 F9 C7 32 AC 64 70 C0 37 64 00 82 12
+    E2 1B 14 67 77 8B 18 19 89 F8 88 58 21 1B 45 DF
+    87 99 CF 96 1F 80 0D FA C9 9E 64 40 39 E2 97 9A
+    40 16 F5 45 6F F4 21 C5 B3 85 DA 2B 85 5D A7 E3
+    1C 8C 2E 8E 4B A4 1E B4 09 5C B9 99 D9 75 9C B4
+    03 58 DA 85 62 A2 E6 13 49 E0 5A 2E 13 F1 B7 4E
+    C9 E6 9F 5B 42 6D C7 41 38 FF CD C5 71 C3 2B 39
+    B9 F5 55 63 E1 A9 9D C4 22 C3 06 02 6D 6A 0F 9D
+    E8 51 62 B3 86 79 4C A0 68 8B 76 4B 3D 32 20 0C
+    C4 59 74 97 32 A0 F3 A3 41 C0 EF C9 6A 22 C6 3B
+    AD 7D 96 CC 9B A4 76 8C 6F CF A1 F2 00 10 7C F9
+    FA E5 C0 D7 54 95 8C 5A 75 6B 37 6A 3B E6 9F 88
+    07 4F 20 0E 9E 95 A8 CA 5B CF 96 99 98 DB 1D C3
+    7D 0D 3D 91 6F 6C AA B3 F0 37 82 C9 C4 4A 2E 14
+    E8 07 86 BE CE 45 87 B9 EF 82 CB F4 54 E0 E3 4B
+    D1 75 AE 57 D3 6A F4 E7 26 B2 21 33 2C ED 36 C8
+    CE 2E 06 20 3C 65 6A E8 DA 03 7D 08 E7 16 0B 48
+    0C 1A 85 16 BF 06 DD 97 BF 4A A4 C0 24 93 10 DC
+    0B 06 5D C6 39 57 63 55 38 4D 16 5C 6A 50 9B 12
+    F7 BB D1 E1 5B 22 BC E0 2F A0 48 DD FA AC F7 41
+    5F 49 B6 32 4C 1D 06 7B 52 64 E1 12 5F 7F 75 42
+    7F 31 2B D9 34 6E B4 E4 00 B1 F7 CB 31 28 8C 9E
+    3F 73 5E CA 9C ED 0D B8 88 E2 E2 F4 02 24 3B D6
+    46 18 A2 3E 10 F9 C2 29 39 74 40 54 2D 0A B1 B2
+    E1 0D AC C5 C9 5E 59 7F 2C 7E A3 84 38 10 5F 97
+    80 3D BB 03 FC C0 FD 41 6B 09 05 A4 1D 18 4D EB
+    23 89 05 77 58 91 F9 35 01 FB 41 76 A3 BD 6C 46
+    44 61 D3 6E E8 B0 08 AA BD 9E 26 A3 40 55 E8 0C
+    8C 81 3E EB A0 7F 72 8A B3 2B 15 60 5A D1 61 A0
+    66 9F 6F CE 5C 55 09 FB B6 AF D2 4A EA CC 5F A4
+    A5 15 23 E6 B1 73 24 6E D4 BF A5 21 D7 4F C6 BB
+
+SHAKE-256 sample of 30-bit message
+
+Msg as bit string
+    1 1 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0
+
+Output val is
+    46 5D 08 1D FF 87 5E 39 62 00 E4 48 1A 3E 9D CD
+    88 D0 79 AA 6D 66 22 6C B6 BA 45 41 07 CB 81 A7
+    84 1A B0 29 60 DE 27 9C CB E3 4B 42 C3 65 85 AD
+    86 96 4D B0 DB 52 B6 E7 B4 36 9E CE 8F 72 48 58
+    9B A7 8A B1 82 8F FC 33 5C B1 23 97 11 9B FD 2B
+    87 EB 78 98 AE B9 56 B6 F2 3D DF 0B D4 00 43 86
+    A8 E5 26 55 4E F4 E4 83 FA CE E3 0D D3 2E 20 4F
+    FF 8C 36 BB D6 02 A5 76 D1 39 08 9C 75 A8 05 02
+    66 FC BF 72 1E 44 43 DE 46 45 83 29 22 EB 8A AE
+    39 D1 F5 72 84 53 64 81 7B 00 33 54 38 99 94 00
+    23 F2 E9 65 A6 0A 80 EB 22 1E B1 9D C5 7B 12 12
+    91 56 4C 6F 69 35 83 B3 AC 7C 6F 27 2F 4F 67 A1
+    9A 76 78 D4 23 4B 0B F4 A2 EB C0 8A A2 35 B9 78
+    8D B7 87 16 1F 66 17 02 28 65 C0 EF 9A A5 33 80
+    2D 13 6C DB C7 AE BA 53 2A CF 1B E1 83 B0 29 5A
+    B0 E3 3A 2E F6 9B E3 56 DA AF 30 96 87 15 3E 2F
+    99 A1 24 36 09 D6 03 12 6A 8C 82 3E 88 43 E4 59
+    BF C7 2B 30 69 1C DC C3 DD B2 7C F0 28 AF D5 1E
+    44 37 EE 3B 71 C0 C1 EC 87 A9 34 36 F0 C2 47 B7
+    E8 C5 0C E9 68 25 C9 70 29 99 7A 74 C3 18 AF AC
+    AA 18 A0 18 0B C7 F2 F0 F1 C5 E7 EF 1A 2D 18 3A
+    C7 EE 7E 49 15 C3 B6 8C 30 97 8A B6 C4 28 19 34
+    41 DF 47 05 B7 22 CE 25 A0 8A 1F AD CA 0E EF 1F
+    AF E8 3A DF 13 02 1D 52 0D E5 C8 27 FF 9A 97 B7
+    55 46 19 3A 9B 92 3F 05 90 38 5D C4 BF F7 C4 9D
+    49 15 B5 A3 65 DB 4C 84 DD CB 18 5D E8 F9 EE B3
+    34 96 5A 42 F1 38 1C 8B AD C2 2B A1 F8 EE 4C 0E
+    4D AA F7 A8 8E 7F 42 DD B8 14 8F 3B F8 D3 B8 D7
+    4F 09 81 55 A3 7C B4 CB 27 87 6B 85 DA 60 2E 5C
+    78 9C 10 E0 3B E7 34 07 BA B8 C4 92 13 F8 C7 4E
+    12 66 CE 9B 11 28 6E 67 4C A9 C1 0C 9C 99 55 04
+    9A 66 E9 05 1D 9A 2B 1F C9 AF E2 67 98 E9 CE C6
+
+SHAKE-256 sample of 1600-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+
+Output val is
+    CD 8A 92 0E D1 41 AA 04 07 A2 2D 59 28 86 52 E9
+    D9 F1 A7 EE 0C 1E 7C 1C A6 99 42 4D A8 4A 90 4D
+    2D 70 0C AA E7 39 6E CE 96 60 44 40 57 7D A4 F3
+    AA 22 AE B8 85 7F 96 1C 4C D8 E0 6F 0A E6 61 0B
+    10 48 A7 F6 4E 10 74 CD 62 9E 85 AD 75 66 04 8E
+    FC 4F B5 00 B4 86 A3 30 9A 8F 26 72 4C 0E D6 28
+    00 1A 10 99 42 24 68 DE 72 6F 10 61 D9 9E B9 E9
+    36 04 D5 AA 74 67 D4 B1 BD 64 84 58 2A 38 43 17
+    D7 F4 7D 75 0B 8F 54 99 51 2B B8 5A 22 6C 42 43
+    55 6E 69 6F 6B D0 72 C5 AA 2D 9B 69 73 02 44 B5
+    68 53 D1 69 70 AD 81 7E 21 3E 47 06 18 17 80 01
+    C9 FB 56 C5 4F EF A5 FE E6 7D 2D A5 24 BB 3B 0B
+    61 EF 0E 91 14 A9 2C DB B6 CC CB 98 61 5C FE 76
+    E3 51 0D D8 8D 1C C2 8F F9 92 87 51 2F 24 BF AF
+    A1 A7 68 77 B6 F3 71 98 E3 A6 41 C6 8A 7C 42 D4
+    5F A7 AC C1 0D AE 5F 3C EF B7 B7 35 F1 2D 4E 58
+    9F 7A 45 6E 78 C0 F5 E4 C4 47 1F FF A5 E4 FA 05
+    14 AE 97 4D 8C 26 48 51 3B 5D B4 94 CE A8 47 15
+    6D 27 7A D0 E1 41 C2 4C 78 39 06 4C D0 88 51 BC
+    2E 7C A1 09 FD 4E 25 1C 35 BB 0A 04 FB 05 B3 64
+    FF 8C 4D 8B 59 BC 30 3E 25 32 8C 09 A8 82 E9 52
+    51 8E 1A 8A E0 FF 26 5D 61 C4 65 89 69 73 D7 49
+    04 99 DC 63 9F B8 50 2B 39 45 67 91 B1 B6 EC 5B
+    CC 5D 9A C3 6A 6D F6 22 A0 70 D4 3F ED 78 1F 5F
+    14 9F 7B 62 67 5E 7D 1A 4D 6D EC 48 C1 C7 16 45
+    86 EA E0 6A 51 20 8C 0B 79 12 44 D3 07 72 65 05
+    C3 AD 4B 26 B6 82 23 77 25 7A A1 52 03 75 60 A7
+    39 71 4A 3C A7 9B D6 05 54 7C 9B 78 DD 1F 59 6F
+    2D 4F 17 91 BC 68 9A 0E 9B 79 9A 37 33 9C 04 27
+    57 33 74 01 43 EF 5D 2B 58 B9 6A 36 3D 4E 08 07
+    6A 1A 9D 78 46 43 6E 4D CA 57 28 B6 F7 60 EE F0
+    CA 92 BF 0B E5 61 5E 96 95 9D 76 71 97 A0 BE EB
+
+SHAKE-256 sample of 1605-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0
+
+Output val is
+    98 D0 93 B0 67 47 57 60 12 4F FB 92 04 A5 B3 27
+    C6 BB 05 C5 4F F2 34 F0 B4 3F AC 72 40 41 51 66
+    A8 C7 05 EA 0D 73 9F 08 08 B0 65 76 D9 96 66 2C
+    1F 37 66 94 D9 8F 51 57 19 B6 64 07 72 0D CF 78
+    1C 51 CD 56 EF 8B 61 0C 66 8D DC 1A C1 C2 C4 29
+    EA 4D 6F 27 4A A7 A7 73 BF 8B 0C AB 30 6F 1E EE
+    2A 17 1B 91 33 4E A0 FA CD 2A AC 1F 51 D4 D5 EB
+    0E 63 A4 E6 75 4E CA FE EC 24 6B 7A AF 58 D0 E0
+    A9 74 C7 FF 40 58 BD BD ED B3 3E D0 4B 0F A4 5D
+    70 C7 C8 4F 3D A1 3E 4F 7D 1B ED DB 53 4D 37 E5
+    AB DF B2 9F 2B 44 C4 FB 0D 6C CA B8 31 D9 0B A4
+    6A 00 53 06 62 F9 07 DE DD 47 9E 9B 54 28 E5 E2
+    DB 80 40 B0 E2 B1 F1 74 CE 34 7F 32 A0 6A 5A C2
+    2B 19 AA FE 92 7B 88 78 D0 C8 10 3A 4D 2F 19 E3
+    23 36 C6 4C FA DC 1B 9A CB 39 78 A8 29 85 71 DC
+    D8 9C 36 A6 56 92 81 6D 0C 61 CE 0E D1 79 42 36
+    70 17 BD 40 F5 9D FB AE 34 63 58 27 92 0A FE 7A
+    27 BF 56 70 09 A1 38 40 3F 06 B6 E4 DE 94 DA 07
+    7D B4 97 73 C2 35 46 61 19 42 6F 79 88 8D 3A 81
+    B4 07 DF EB A8 7E 01 CD 48 F9 0E 01 B6 F9 02 43
+    C4 01 25 DE 47 E8 C8 F3 E6 EA 33 88 CB FE EB 36
+    54 1E F2 3D 2C 83 48 45 8E A2 8C AA 50 66 F4 98
+    37 76 F0 CB 2F DC 66 04 9C F8 8A C8 EA E5 12 12
+    AA CE 86 7B EA 4C 3C AE E4 4F 14 7A 9B F9 9D 04
+    87 4E 87 22 D0 3D 3F 5F F6 EF 3B EB E7 64 2F E4
+    91 6C 5F 10 FF 3F D6 13 87 D5 D9 1B CD 32 F9 E8
+    E4 59 3D CA AD 23 EC CC 05 D2 FC 9B E2 C1 CD 63
+    0E A1 23 DC A9 CB 69 38 D6 0C DD ED C1 1E 1E 9B
+    C9 D2 68 A5 45 6B A9 CC FF 18 59 7C 5F F9 73 57
+    08 41 3B 9D 84 B9 F4 72 19 37 CC 65 95 71 27 97
+    53 2B 48 D6 F1 A2 D1 72 3B 07 D5 46 0B C1 39 16
+    D9 6E 88 18 07 13 AC 33 D2 C2 32 E3 5E 76 4E 04
+
+SHAKE-256 sample of 1630-bit message
+
+Msg as bit string
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1
+    1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1
+
+Output val is
+    8A 83 25 07 9B 0F C3 26 5D 52 F5 98 55 CA FE 65
+    5D F4 38 AA 63 9F 6F EC 99 1F 24 94 33 0C E3 2F
+    A3 7F 7D B9 0F 69 66 D8 E4 A4 6E 50 C5 ED E5 7B
+    9B 8F 08 2A 96 62 7F 73 04 75 02 9A 61 92 29 D8
+    4F 43 2E D6 9F D0 59 23 4D 4D 7D D3 58 E8 39 3F
+    6A 36 A4 5C CF 04 1F 90 FC 0A 4E 58 02 D7 30 63
+    D3 65 31 33 6A 00 90 EC FE 1A 4D 4D 29 AA 82 4B
+    A4 2B 49 37 B4 BB 98 F4 F3 3A 0E 3B D8 B5 11 E6
+    95 28 D5 95 37 11 0D 75 21 FB 78 AC A0 18 DF 76
+    16 0F 54 A3 42 1B 84 14 92 64 ED 03 2F 6D CE 46
+    7A 73 1A 8E 34 04 8E 3A 46 E9 80 39 DF 3C 32 8D
+    EB FB E5 D1 BC 8B E7 FF 4E F8 91 7B 01 F0 B7 89
+    36 72 49 2D 6E E5 C7 1D F2 D0 53 1F 8B 68 47 64
+    BA 0A 2B 57 EC 6A 4F 60 BA 4F 36 FE 2D B0 E6 5A
+    D7 AA 5F 14 F3 EF 9F 34 A0 AB 5B C3 3D 48 87 33
+    BA 36 BF 4B 2B 4F CE 02 8E FF 8C 6C E0 3B 19 2C
+    F0 75 CC 9F 00 D2 9C 0E 06 C3 5C 44 89 D2 7F 07
+    FA 49 A9 1C A9 24 71 E3 4D AB 77 87 AE 24 A6 E0
+    F3 09 EF 0B A5 3F 7C 8B 29 92 52 0A 07 BE DD 50
+    9A 0B 6D BE A5 70 A5 96 0E D6 24 82 6D D8 EC D1
+    91 5C 87 32 7E 74 49 1C 40 5A 74 11 C1 2C 0D 44
+    97 51 26 89 BD 7F 5A DB ED B0 2C 6D 2E 68 47 4E
+    8B F3 1B 88 40 40 81 8F 4B CA 03 A4 52 17 EA C7
+    08 3A D3 A3 3C B8 47 7A 04 C9 E3 26 6A 13 34 77
+    DE 45 E7 18 30 A4 0E B0 D0 75 AF CC FC D9 DC 54
+    8D 0D 52 94 60 EA 7A C2 AD AC 72 2E 76 78 EF 59
+    7D D3 B4 95 BD 7D 1A 8F F3 94 48 BB AB 1D C6 A8
+    84 81 80 1C F5 A8 01 0E 87 3C 31 E4 79 A5 E3 DB
+    3D 4E 67 D1 D9 48 E6 7C C6 6F D7 5A 4A 19 C1 20
+    66 2E F5 59 77 BD DB AC 07 21 C8 0D 69 90 26 93
+    C8 3D 5E F7 BC 27 EF A3 93 AF 4C 43 9F C3 99 58
+    E0 E7 55 37 35 88 02 EF 08 53 B7 47 0B 0F 19 AC
+
diff --git a/crypto/test/data/openpgp/bigpub.asc b/crypto/test/data/openpgp/bigpub.asc
new file mode 100644
index 000000000..a3b0766c4
--- /dev/null
+++ b/crypto/test/data/openpgp/bigpub.asc
@@ -0,0 +1,15124 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: SKS 1.1.0
+
+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
+gSCaHIULIYhkBBIRCAAMBQJRn3jMBYMHhh+AAAoJEICU1Dqrdkd9+3AA/1aE7e5fTpcnRxzz
+T/vqRn8ZP7f9GI0UvA2gBAgfuzoRAQCP5kYQJoHJov66QdUlS/m0eRswwTZxMerp3jtuTj4Z
+zohqBDARAgAqBQJC+KU9Ix0ATWlzdGFrZSB3aGlsZSBkb2luZyBsb2NhbCBzaWduLi4uAAoJ
+EBVNeqxcW1sJ4vcAn3ERVGMi6UpBkZ44TlZJHvJOoVyrAJsFvfGdOvNSArCrd7wEHoxmMDTH
+TIh1BDARAgA1BQJDAKDvLh0Abm8gbG9uZ2VyIGZlZWwgaXQncyBhcHByb3ByaWF0ZSB0byBz
+aWduIHRoaXMACgkQvZIxoLH+UIuZKgCgvA/Be0txtOPZj+XORIjL2+ty7NQAoIQFv2pJySqb
+bUq8+B8P0h0WYClZiHUEMBECADUFAkMAoPcuHQBubyBsb25nZXIgZmVlbCBpdCdzIGFwcHJv
+cHJpYXRlIHRvIHNpZ24gdGhpcwAKCRDJwzyyhrFKOLZVAJ9LjQ+sFuDIk2ekajKJdGa6IrZ5
+yQCgjxXCS0IoX4fTr1lFYSGgWDHAaGOIdQQwEQIANQUCQwCg/S4dAG5vIGxvbmdlciBmZWVs
+IGl0J3MgYXBwcm9wcmlhdGUgdG8gc2lnbiB0aGlzAAoJEF8I49t3ADyqCPEAoM/UfFPGDqx8
+fwgX4E6bcFHqWe3/AKDnMab2YjUkZyWQ5SorQxPEBXmWuYiMBDARAgBMBQJGgTMRRR0ASSBk
+aWRuJ3QgcmVhbGx5IGRvIGEgZ29vZCBjaGVjayBhYm91dCB0aGUgYXV0aGVudGljaXR5IG9m
+IHRoaXMga2V5LgAKCRD8sLtcXx+/cMwWAJ9fIufE249FIu+9hXdGEYtNqHgPiwCeLTir5fID
+sEA3DspaFO2bfrTg0YCIkwQwEQIAUwUCRo9FTkwdAEkgZG9uJ3QgcmVtZW1iZXIgd2h5IEkg
+c2lnbmVkIGl0IG9yaWdpbmFsbHksIHNvIEknbSBmaXhpbmcgdGhhdCBlcnJvciBub3cuAAoJ
+EFWOCYKC0iQcTIEAnR3e3pOl8M8BKC2ZoqjO/64JLEW1AJ9akHVwIFv+4R72Ti6h4aOtPC2v
+TYiVAwUQQbrTnCnYReUHfp2tAQEWTQP/eTYr2U/tgLzbwXuFJcZB1j/CLL33eMiv8YYpsoka
+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
+IPOqwVra6aGjIn6IpFqgUzE9PO56uzr+2/DWbrKvJCjKvqOaPDxHTKO30Cwdtivibl8pwAIt
+2kUAn/FVt1w9Hp+cJTAKt5tAo+NBCsGDEj/+veacGxYKcp8OEyKBc1gEdqNPiukTNsi6izvF
+XjRluy/43GkJmCrpjsRwF+gNXuaHArMXwgfem2USM7ek0/80m4hX0qGk1LvocMnjxLkIaKoC
+tmLO8PU2wdc4p5RrG1dGO6rtvmYZZA22OatSTwckoZm6A7VhB/R5QQxEWktNGbuCsoYjXqA8
+awOsdfNV6IZrFU7a3qiW63TzV8IDsr19R4HygPT49UUixMb2/LxpDEE5jFf8KfSPX44ZQkoP
+WxccJoY8e32/McgtHay5jw2lyPSpScSaf38bNFtpPa6zwFOQwzoDCWFpp1TIdwL/frJy3cQQ
+YchT8bCwspk6WOS02GBSAALztJLLjdhCAbCZipRU9Aqg9gUlEulL8tBbsL0KjSNq8W1zqrWc
+7CQsfri+gI/lNkwhvC/smPdtJFMcCUnNcrZu1NXgVOE/ARPxQeew1+JUHfnVQR6LHYoDw7wZ
+u+L/////////////////////////////////////////////////////////////////////
+////////////////////////////iQIVAwUQQcNjVImkvBtYMMYnAQLnkBAAvxkjHtEg86rB
+WtrpoaMifoikWqBTMT087nq7Ov7b8NZusq8kKMq+o5o8PEdMo7fQLB22K+JuXynAAi3aRQCf
+8VW3XD0en5wlMAq3m0Cj40EKwYMSP/695pwbFgpynw4TIoFzWAR2o0+K6RM2yLqLO8VeNGW7
+L/jcaQmYKumOxHAX6A1e5ocCsxfCB96bZRIzt6TT/zSbiFfSoaTUu+hwyePEuQhoqgK2Ys7w
+9TbB1zinlGsbV0Y7qu2+ZhlkDbY5q1JPByShmboDtWEH9HlBDERaS00Zu4KyhiNeoDxrA6x1
+81XohmsVTtreqJbrdPNXwgOyvX1HgfKA9Pj1RSLExvb8vGkMQTmMV/wp9I9fjhlCSg9bFxwm
+hjx7fb8xyC0drLmPDaXI9KlJxJp/fxs0W2k9rrPAU5DDOgMJYWmnVMh3Av9+snLdxBBhyFPx
+sLCymTpY5LTYYFIAAvO0ksuN2EIBsJmKlFT0CqD2BSUS6f//////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//////////////////////+JAhUDBRBBw2NUiaS8G1gwxicBAueQEAC/GSMe0SDzqsFa2umh
+oyJ+iKRaoFMxPTzuers6/tvw1m6yryQoyr6jmjw8R0yjt9AsHbYr4m5fKcACLdpFAJ/xVbdc
+PR6fnCUwCrebQKPjQQrBgxI//r3mnBsWCnKfDhMigXNYBHajT4rpEzbIuos7xV40Zbsv+Nxp
+CZgq6Y7EcBfoDV7mhwKzF8IH3ptlEjO3pNP/NJuIV9KhpNS76HDJ48S5CGiqArZizvD1NsHX
+OKeUaxtXRjuq7b5mGWQNtjmrUk8HJKGZugO1YQf0eUEMRFpLTRm7grKGI16gPGsDrHXzVeiG
+axVO2t6olut081fCA7K9fUeB8oD0+PVFIsTG9vy8aQxBOYxX/Cn0j1+OGUJKD1sXHCaGPHt9
+vzHILR2suY8Npcj0////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+/////////////////4kCFQMFEEHEK3dWL3RV6ir3SwECB3IQALkhb/fDBBhTKimR+5Qzcj7z
+zSZ2TfMNBwq+Wr6p5X7mU/1w2SHYNmOLGDs4Eaqto5CTJ2jS4U4B/Clow0LOTViY7pg3Yrzf
+gQPT+omA1aW+if7UNCmEYvUBSLSJXOWUAdMfsurWPuRbbnjczurpevoZCvl1RwN0wPRehfAC
+Fyw0FlJoae+lBGUGgpF9kJyZ1RdguNd0AneCCr+XsbeGJ8sxpxYyLci3A2Am5pioxDbz/l6f
+dKV/RwjdmOjdlD35k8vUCoGZQTjbB4hLpWkfZecmF0TXPqT41+UZoRfx4dsG4146qEsGGDQM
+KGFXd8Rm5yNvbmgso+gDI4QKLukLCkq2ZoZ0mYb5QeMbAcQ9iUMnspFaIn7K9yyBqNCs55Qu
+3fKfKbMnBnS4GoYojU6179wlFkKJV6cO92NCkNvv2Do40ucvGqpfaa4nDvQf35dkj6xukN9R
+13d1RmAnmBxdx8e+mpThUB9lwwhoIRUwKFaSFGoVncMaIfqw5tj9DBm8wAGN9lmJakM12C8v
+VA2zKH0L7KPxb2u036yBPxPzFJfUATmioUhKHBky9xhGfBUSS8DHr4hBygvCYLcyaYdh9cyV
+juTdM8IYdVxFaaWhzxhNdK1G/iZGxFq/CPS7MgfCrQhwg57/rpWonaM3pRc8dGYp4VC5c96X
+5yPOIk5Yxr6wiQIVAwUQQc9h1wRVjUj9NCi0AQKfvQ/+LCqte1ajVbLVGp5u+Q5bAxTO3nb2
+s88MYOmprZ0sDXxNT/vPrNCIx0sjbPo/tPdBrVMrVPA/VaTCMRpuWVLcBlqOouk6OfXMMRbu
+7uk46p0dJyIrbgdQOesruIMtdG5KBeaBzfFG6lPdiG2y6a+38BOwgyqvwjUzeIyucqq6gq+9
+5ufi58MzBPi1tK776loTdNYWOJBJhf+ue37UNHirtThYzEpk+NdA/k7VVp2919DfrDdQsyok
+vyCp2OzryP1IgMeJjtGZ1+yB+4cWXMcJ3idLcNmnPmE4xvI33r5myKULD71ooU7cIFa8MbE8
+ABGRMSKOZhOTdsF0oBzkPDcVljOjDCy2b498ThCYNKG0SFKhPXjB6yaWNhL4hkSIZLYq1emp
+aNNZcgQ/TVWmJOL2kMEtQiW6yk85gOS4+l6JpDQDK7/w5NlvFbL8tO4W/qZiMpc8N4Guklmb
+HaNiQROGnyxmas9ZhAV5ZaYALcKiKfI6OAmmlvOb3xEybrdtYLFuQtPdBnwjHo/5o88EmwV7
+YRf2FF7cwD0zy100sBchZxBL+wm3UB/+a6Nlk+DFkGOqp+JsFTaK2o4iUnX5IiPpNPC/5chF
+FqQ5YDCgFo9X4DY1A9bXmLXvw05kH8DF4VhbxZn6SslH5MjA2jETuEbB/9zXYjFM+ysQ9D5z
+CxToMf+JAhUDBRBB1tAyl1IjhnySjoUBAh9ND/9xWGS8AgG0faic0vzr1XvmCGGjrTW+I3YL
+r7WhJ5oU/RKSVgpOKfoHj2vqRdvVKCRuzi3+dYhro4vaeLn8i/oNBTbG2D4Xjv941PS7N01g
+FT61oJwTpdkr9EZkPEsbT1fm6qYgLe0+Z1vf8QKfJapO5Bs05NWm9AgTm2R7oNB2fBndkbLI
+TiDFtKWlMc1VyZWMN0e7zWsN4Y47K41HBBm2cpXO2mL81vi2/hygHXxk3g2U7psKlyVacGV3
+b/fs2zaIxeLOA3uC87LfEGqZhr3v+G4GvJ3puwbi8PompeIdRGm0HmIwwK49ltF+69Ej1/Lk
+OD5uXCMGnOoNOdAGaiIEWjmEQeTB9ONMYZbqqfAsMtcf6UEE+tVd+fj89PCbpMU+YJX7qFlv
+hOCiI9GIhPftKyOsoSpzLXAj+kuBMSq09qAb77kHiMOzuRNlvpkBjkWKl2nHWUzSi/8Uc+4+
+7yajKQDTs/eqvMYfsb/MBVf4f9Te07Jep2VP9SWVht/Tniza+VErrwNJejDVci035uEV7A+o
+M4w2ACee5kFeXjZj4/FOwUmCyvx1RslBpK3aWwy2auvhJV4By8/WUs2UtnGapb+xF7TvviOD
+87XSiTFYEu8P3X5q5CgqKXCbHpkncyXek8rqpqONXj6/P5H4JFAUGkhPZ3okSsgCV72PJ7/S
+TIkCFQMFEEHW0DKXUiOGfJKOhQECH00P/3FYZLwCAbR9qJzS/OvVe+YIYaOtNb4jdguvtaEn
+mhT9EpJWCk4p+gePa+pF29UoJG7OLf51iGuji9p4ufyL+g0FNsbYPheO/3jU9Ls3TWAVPrWg
+nBOl2Sv0RmQ8SxtPV+bqpiAt7T5nW9/xAp8lqk7kGzTk1ab0CBObZHug0HZ8Gd2RsshOIMW0
+paUxzVXJlYw3R7vNaw3hjjsrjUcEGbZylc7aYvzW+Lb+HKAdfGTeDZTumwr/////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+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
+RqLP93kxsWzK1ynuWv2Wz6J+SXkWWz/70YydXrLqLmv5su2yQg51d35j7kDjBU1Vj9RriNza
+dmB7PGSB48RYcjswsZw3CFNojrfK0gl4eRlUiEkHyWX60gb68eO/vVzz2lisoorD2IkCHAQQ
+AQIABgUCQvF1agAKCRAIeL6M95ASakJSD/9LRPB2WZpt1rKSD8quwjUO84ie/WaLFGS4Cme8
+NI65GfglW6AyGjnsry3LyubZeGHytzD4Q9EcmVY3RSTwD5h+Ppya4drbiDEI/77OUuBj8qER
+nyJpNGhSn7ug+w1Iu7nkPkGU7aEQtTnKvsNksXDQb2OiH4hboHsbS/aeCYEHPdnmElKSmK4u
+RPgka4WworGk8nbD2IpRwhlSugmSOWCHcDgItnKe75+erf//////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////4kC
+HAQQAQIABgUCRHORmwAKCRC/tlXydUU8WCHtEACkvnfnBsV2yC9xElTs98h5fbiFgYu8v7R6
+toPlg2NEXUckfl7K/uVf/qq379A5uQeYdCf1gDAXBXjdjHDKrkx0my3GOMCU1W9A04GaD2j/
+2ogNqbE8yRF+PNsrS3wZvOqrnT0YPT9yICuBaPusv5JEtpniqStu8MlUWx8kWAUmq0V3Yq4y
+3w+m0EXuS6on5kfFqjv4DIskYwsxjUsY3I3f4ZdnGGWuQZYYHdXw6DOVx0dPk4eczBb5VJ6O
+h9GK8a/0yHoNzlg3hsyi0kadHHVNiwDb4qnKXJmGg33i1Qa1bpgD5v2SIV5N+DmaX9H3PML4
+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
+AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3
+ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj
+pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6
+/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3
+AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2
+Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJma
+oqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6
+/9oADAMBAAIRAxEAPwD2aiq1/fQabZyXdy+2KMZJrhr74s2UZK2Vm8pHRm4FaQpTqfCiZTjH
+c9CoryC4+KesTNiGGKFfzqez8V+Jr9fMSUlQc5Xj+tb/AFSoleVkR7aL2PWKK8w/4THWrf78
+oLDqCKng+Jd1EQLm1SQdyvBpPCVVsrh7WJ6RRXOaF4107XJxbIrxTkZ2t/jXRA5rnlFxdpI0
+TT1QtFFFSMKKKKACiiigAooooA5j4huV8I3WD1wD+teHV9A+JdPh1TSjZ3BcRytyUIBGATxk
+H0rgn8AaQp4mvfxlT/4ivQwuIhSg1I56tOUndHni/eFel+DdR00aasc8yxOg5z34qtB4F0B3
+ZJLu+Rh0+df/AIirLeB9GhOFv70Y/wBpD/7JV1q9GrGzbJhTnB3MfXri3nv5XtceVk4I6Hmu
+fmfk12snhTTEG3+0LzHXqn/xFM/4QrS5FDfar0g9/MT/AOIq4YqlFJJidKbdzn/CM/leIoXL
+BQOpJx6V69Hr+kJHiTVLQMOo85cj9a88uPB2mWlu08b3MjqyjbKylTlgOgUetbuk6ZFakSKp
+XK4+UcfjXFiakak+aJtTi4qzOpj1/Rpm2x6rZs3p565/nV9WV1DIwZT0IOQa54wI6nG1h7c1
+hahpskk5mtZXtXXIV7VzGcfUdfxrnsaXPQKK4WDxTqumOqXY+2RdMyAK/wCDDj8x+NdTpWu2
+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
+lNQ6q3ZHfdW/APsFH6fcoWO7ppGEzUHEee7thGHVM3A8GmxHRGU4s43QsAD/fpHhn6e52HKl
+2seDy39+2WVG9/Guxgmmn0IpBfVSl4GIjAQwEQIATAUCRoEzEUUdAEkgZGlkbid0IHJlYWxs
+eSBkbyBhIGdvb2QgY2hlY2sgYWJvdXQgdGhlIGF1dGhlbnRpY2l0eSBvZiB0aGlzIGtleS4A
+CgkQ/LC7XF8fv3DPZQCfZ9qhpJmT1IPzF/Ag4/LdDS+w/qMAn2HYZ037ah7ddgS/gzLausNL
+EqMsiJMEMBECAFMFAkaPRU9MHQBJIGRvbid0IHJlbWVtYmVyIHdoeSBJIHNpZ25lZCBpdCBv
+cmlnaW5hbGx5LCBzbyBJJ20gZml4aW5nIHRoYXQgZXJyb3Igbm93LgAKCRBVjgmCgtIkHMWI
+AJ0QIVAUAGN4y7U2wf/I2o0/yCwKowCgiBXY7Or3H+l1zRFuQLYoeYV76fSInAQQAQIABgUC
+UC5vlQAKCRBC5j+f0+THVJ/CA/wJBe0Og0AfQf/uHKm8J44he6vmcrstjEos/JmtoFfFQ+Vt
+8Yij6L4XMGDZ0eBAkF9D9ND3d7fc3B8IVZJ2uU4lx3qTNqiSCTBBhzGPqSr/ekTeRoIf42f5
+Yyx8ln4MLMZCAO4MHmHRiNwNm1EEJXhQU2+G4eY3Xk+oXwLXtJw2v4iiBDARAgBiBQJCP/Jk
+Wx0ASSBmb3Jnb3QgdGhhdCBJIGFjdHVhbGx5IGhhdmUgbm8gd2F5IG9mIHZlcmlmeWluZyB0
+aGF0IHRoYXQga2V5IHJlYWxseSBiZWxvbmdzIHRvIHBncC5jb20ACgkQMBkOjB8o2K5CMQCf
+RFPNmaGsRmYp2CbgpRMLvhlJPHcAn1iMZQKf+lrIyauTce8ucFlGbgx5iKIEMBECAGIFElIv
+4mRbHQBJIGZvcmdvdCB0aGF0IEkgYWN0dWFsbHkgaGF2ZSBubyB3YXkgb2YgdmVyaWZ5aW5n
+IHRoYXQgdGhhdCBrZXkgcmVhbGx5IGJlbG9uZ3MgdG8gcGdwLmNvbQAKCRAwGQ6MHyjYrkIx
+AJ9EU82ZoaxGZinYJuClEwu+GUk8dwCfWIxlAp/6WsjJq5Nx7y5wWUZuDHmJARsEEAECAAYF
+AlGDXMEACgkQWxImvOXpKyrWSQf4nTn1E0TJN+LD8dDt2C4BEBi22TDX36Zm3oqsH3UbwjE+
+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
+/gJ6imAz/EhFB1JGLsx5Uf9A9vkAecL1JWgoc6ccIouOrBK95d40DLL51zeZF6G8d3kdRveD
+h02bcZJVDNeJk38+B3QyjJmfoeVLAK0MEIeBQS9ej9oK+OBksmeumcT1VpAaaiQfJa3GU76w
+i2ey8sRqRv6QAFee2PIwD2rhJWQkCpXgNuevYaRFl6dTrBfOizIqxEoLnYHVMBnJ95tqaSyy
+INdGXnNrFKHSNa+AHzz7/YamXgDpN1hEKfh/Y8D9ZyvVGbnfPTjIIyLYamdtCoPR2YaKJGCF
+VYkCHAQQAQIABgUCQcKD8AAKCRCq4+bOZqFEaDKtEADINSsH2euET5sYGISy/GXl3nL3PHE2
+MAUK5y2Lch9XtNDGQm0YSLPNa7pER/3UZJfn4TINcesu5LFUOIG4o7No3MolN97/////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+/////4kCHAQQAQIABgUCQkQM+wAKCRDcO5tBqsVPo19CEAC39tmTO0cmvcJn1vXBC/fEUkJC
+9kC5bV+tGU3aKf42gr0VurJASi70v5rpKsJ+GC3uTbrmVX8QVrYRi4mpAPgC+gO52Wz2WUJ1
+AmbLW9c3o/+weOeAg3gUT0R9ukrGWg8LBbQXL3KT7DjWwDuqFD8VJ8Zdiux1cnV4uLzDjGzb
+go5hZqEUla6jQuqNlJvABmel8P5G5YTt6sL+iIEDMABvNlQZx3JbhgYegY9Bv6rrGWzcVU/b
+hJLQLiD/JGAxjz9XjU4JVnn9UtVffkQgAhZuWvH41pi1cvUGCUbVM1gskL2lkmnDnG+jnVRk
+sVGw08NZrP7H/FglGJKOOa9OSSoW/10iGAJBJ1PYGivjcKBq4MlIs4F9zb4lvldjTwCgwbSu
+ZG66sna43NSavCNZO2YEZMqjAG2bMxlOLqGD/nC+RGfovKTcMSzxNH9IbMDx7XVmWg5T149T
+kRpBCj989A+G2N90TJEEM5afmBX1r/PDhaqXT4s4WrIVVA5Rldde6khZubuJkV/yQrgPkIHk
+x8q4HBlF3gBjn5HaEJHBYarlNTphTp54GHyGL7OMqEdRFlBqlqnhtc3G8fYGnuyDcy1LsldT
+Z3OTzNQsXknU4d8Y+Fqd3GGcsDO+QP6j4h+34N1Tuua4GL2PKvmGe3T51+7t98IqFDs+7btT
+5miLdwV9jIkCHAQQAQIABgUCQkQM+wAKCRDcO5tBqsVPo19CEAC39tmTO0cmvcJn1vXBC/fE
+UkJC9kC5bV+tGU3aKf42gr0VurJASi70v5rpKsJ+GC3uTbrmVX8QVrYRi4mpAPgC+gO52Wz2
+WUJ1AmbLW9c3o/+weOeAg3gUT0R9ukrGWg8LBbQXL3KT7DjWwDuqFD8VJ8Zdiux1cnV4uLzD
+jGzbgo5hZqEUla6jQuqNlJvABmel8P5G5YTt6sL+iIEDMABvNlQZx3JbhgYegY9Bv6rrGWzc
+VU/bhJLQLiD/JGAxjz9XjU4JVnn9UtVffkQgAhZuWvH41pi1cvUGCUbVM1gskL2lkmnDnG+j
+nVRksVGw08P/////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+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
+JDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3
+eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY
+2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkK
+C//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1Lw
+FWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2
+d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW
+19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A9moqtf30Gm2cl3cvtijGSa4a
+++LNlGStlZvKR0ZuBWkKU6nwomU4x3PQqK8guPinrEzYhhihX86ns/Ffia/XzElJUHOV4/rW
+/wBUqJXlZEe2i9j1iivMP+Ex1q3+/KCw6gip4PiXdREC5tUkHcrwaTwlVbK4e1iekUVzmheN
+dO1ycWyK8U5Gdrf410QOa55RcXaSNE09ULRRRUjCiiigAooooAKKKKAOY+IblfCN1g9cA/rX
+h1fQPiXT4dU0o2dwXEcrclCARgE8ZB9K4J/AGkKeJr38ZU/+Ir0MLiIUoNSOerTlJ3R54v3h
+Xpfg3UdNGmrHPMsToOc9+KrQeBdAd2SS7vkYdPnX/wCIqy3gfRoThb+9GP8AaQ/+yVdavRqx
+s2yYU5wdzH164t57+V7XHlZOCOh5rn5n5NdrJ4U0xBt/tC8x16p/8RTP+EK0uRQ32q9IPfzE
+/wDiKuGKpRSSYnSm3c5/wjP5XiKFywUDqScelevR6/pCR4k1S0DDqPOXI/WvPLjwdplpbtPG
+9zI6so2yspU5YDoFHrW7pOmRWpEiqVyuPlHH41xYmpGpPmibU4uKszqY9f0aZtseq2bN6eeu
+f51fVldQyMGU9CDkGueMCOpxtYe3NYWoabJJOZrWV7V1yFe1cxnH1HX8a57Glz0CiuFg8U6r
+pjql2PtkXTMgCv8Agw4/MfjXU6VrthrCH7NKRIoy8LjDr+Hp7jIosFzRooopDCiiigClqXKR
+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
+Ozv/wAARCACQAHgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL
+/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk
+M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4
+eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ
+2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL
+/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV
+YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3
+eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX
+2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD2aiq1/fQabZyXdy+2KMZJrhr7
+4s2UZK2Vm8pHRm4FaQpTqfCiZTjHc9CoryC4+KesTNiGGKFfzqez8V+Jr9fMSUlQc5Xj+tb/
+AFSoleVkR7aL2PWKK8w/4THWrf78oLDqCKng+Jd1EQLm1SQdyvBpPCVVsrh7WJ6RRXOaF410
+7XJxbIrxTkZ2t/jXRA5rnlFxdpI0TT1QtFFFSMKKKKACiiigAooooA5j4huV8I3WD1wD+teH
+V9A+JdPh1TSjZ3BcRytyUIBGATxkH0rgn8AaQp4mvfxlT/4ivQwuIhSg1I56tOUndHni/eFe
+l+DdR00aasc8yxOg5z34qtB4F0B3ZJLu+Rh0+df/AIirLeB9GhOFv70Y/wBpD/7JV1q9GrGz
+bJhTnB3MfXri3nv5XtceVk4I6Hmufmfk12snhTTEG3+0LzHXqn/xFM/4QrS5FDfar0g9/MT/
+AOIq8YqlFJJydLbdzm/CM+lOIoXLFROpJw6F69H76kJHiTVLQMOo85cj9a88uPB2mWlu08b3
+MjqyjbKylTlgOgUetbuk6ZFakSKpXK4+UcfjXFiakak+aJtTi4qzOpj1/Rpm2x6rZs3p565/
+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
+GL2PKvmGe3T51+7t98IqFDs+7btT5miLdwV9jA==
+=bZzH
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/crypto/test/data/openpgp/longSigSubPack.asc b/crypto/test/data/openpgp/longSigSubPack.asc
new file mode 100644
index 000000000..4b33d422f
--- /dev/null
+++ b/crypto/test/data/openpgp/longSigSubPack.asc
@@ -0,0 +1,15 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Charset: UTF-8
+
+xv8AAABSBAAAAAATCCqGSM49AwEHAgME5D5j84R7ngjS9Zpu631VC3oTDf6vqz3x
+TUcj5qv8zGBGi8LGNriyNadfKlVzl8zWNPSD+cF5C5m6RWAi4Q1htc3/AAAAEmUy
+ZWtleUBleGFtcGxlLmNvbcL/AAAAjQQQEwgAP/8AAAAFglXSSrL/AAAAAosJ/wAA
+AAmQFEbEoxYiUIb/AAAABZUICQoL/wAAAAOWAQL/AAAAApsD/wAAAAKeAQAAOMYA
+/1HSZSJe+umnm9DGuKSql1oxxD6SaHFoh63avcITgcVzAQCb7KazBzAHuHPcMK2z
+GKk+2tO9ehQPgf2w/wry8sF3I87/AAAAVgQAAAAAEggqhkjOPQMBBwIDBPZ2x6Gt
+k2VF5BS4tlYKPVtEazFJ3uiNLngvpfBimk8f6MXwAuU+WXpB1//MUeY6sOODKakh
+PoFYQ74xLMkrlKUDAQgHwv8AAABtBBgTCAAf/wAAAAWCVdJKsv8AAAAJkBRGxKMW
+IlCG/wAAAAKbDAAAQVcBAIrP/tLo8/in5TsYTohMg+RSz3z4KkLpr64+mUJhuD6+
+AQDWN6DYCyQTSHDGKfULHBQjnrQdM2PETTuO7x8LB1mJVg==
+=B6/M
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/crypto/test/data/openpgp/unicode/passphrase_cyr.txt b/crypto/test/data/openpgp/unicode/passphrase_cyr.txt
new file mode 100644
index 000000000..702c84a18
--- /dev/null
+++ b/crypto/test/data/openpgp/unicode/passphrase_cyr.txt
@@ -0,0 +1 @@
+ТестЯ
\ No newline at end of file
diff --git a/crypto/test/data/openpgp/unicode/passphrase_for_test.txt b/crypto/test/data/openpgp/unicode/passphrase_for_test.txt
new file mode 100644
index 000000000..157d99dad
--- /dev/null
+++ b/crypto/test/data/openpgp/unicode/passphrase_for_test.txt
@@ -0,0 +1 @@
+Hndle
\ No newline at end of file
diff --git a/crypto/test/data/openpgp/unicode/secring.gpg b/crypto/test/data/openpgp/unicode/secring.gpg
new file mode 100644
index 000000000..fec9dd5b1
--- /dev/null
+++ b/crypto/test/data/openpgp/unicode/secring.gpg
Binary files differdiff --git a/crypto/test/data/openpgp/unicode/test.asc b/crypto/test/data/openpgp/unicode/test.asc
new file mode 100644
index 000000000..73a3e7f58
--- /dev/null
+++ b/crypto/test/data/openpgp/unicode/test.asc
@@ -0,0 +1,33 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2.0.17 (MingW32)
+
+lQO+BFBcYqwBCAC53iWjgnHOw5eo2N+OWhyz17AnEh45aRtvs/U2cIU+aCM/VXx5
+ig7GN6RcDirgnR/CTAwtdy6V/TFJ9Ej/hKu8hGsL/HCVegzs6hQo3rqXSLaAuH5i
+prdBQ0fFzCB08kbr1VkP9TyTGU6xfoEiDpk33TCbqM5Cx5+7gM5uuquTxE1SkyqV
+hd7M2p6LhvhtlHo5yx/mfPQhBCuRd/HtAXQux+UwFEeVh+1rxcKfygEeMHHkNg3F
+LJBLJW95XxTIxuScJADKhrFPjwtzVWh/chYOoK61O5rvbyRE5epHEOQYCD5X4+IN
+G22eInPaVkx9SS93Wm9UcjWEwfRY/kLDp3TjABEBAAH+AwMCSD3h6GM3cH63FXiH
+nknGYv5N7GZlI+F4m3k2+MbK/OcU2sv98Fa4b78Z5ONLH3oFwIm7NFa7fobmIHyv
+Xmcx9W06CrxpLUroqoRtEFGFrmap6yqAtnqDwtBqk6sar8QSH5HKX4xvBd1AOndk
+Htwk3cD5uN/VaIPEwgOlC+LpvQLQpMTNRpXn2NEvsj6RIEkyWxx/N7+w0B+pfeOY
+dhp8ra6kNs+1N5joMlA7tdBL9pMIiyHVfd077N2A/Fc7ONhDdIJBh9u72nTUa63H
++2jE0LzwFQQrsnz2PRvyWa4XmXVFHOg1DRuoClZ1HXZseOAYtY4u9v+62I3SjVvG
+fVALDVMjwlw1omRupsq5Mn9kuvUcpmc+fcqNJIViO/tm0mFV6Brb802oq5xkstEz
+iEF38cpJJe2WcVwABEEd6T7SZTgzakRMaQAWZ6Avb/yRzBtQ0Nq1mpn22EYHphNY
+JJtNJ3qdtIIV0TR6X034px41Kp97ZFwVPMWsR0NeM+qOQ9w3vixFt9TGdBI8rOYh
+8BSjaglz7FG8svOTfGp/Ja5nLgf3eO4hidQOQkNcRRZ9x+d/ajmZtCm6PBIfTfvH
+R9E7sMjt7CY5QAgqMK4ZwrK9BMrHlk5PLMF0/db53KTgAQcfO/skubU5ko/eWMFX
+gkPxAfCIbN8XP8DjzynxG7V80rngwtcOXLnWOfTce2fDiO1BGCnyu/S1JjRfCA3Y
+IuS5ZVpoIdssPrfXrMEKT2CP9w4R+ERsd869+bYAckaXZ6V7D6rjLYBn4LXCElmJ
+WUvevOIDRIxAUYoFuTY6jnAkQyu3/2bDwXOcGJQ3GDxMojXr8uejyeAW8NUa634C
+hJ8kuFxMXfNVhR9JnodSwe20QsFy7IUnVXergAPEVMSBhsDqFCnWuvgC8pb2dbh+
+u7QgdGVzdCB1c2VyICh0ZXN0KSA8dGVzdEB0ZXN0LmNvbT6JATgEEwECACIFAlBc
+YqwCGw8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOyHJy78uYbSqWQH/11k
+itAUrb6aUKHVyvO0r6NEbQ6TSJCstfJ6N+Euhs14od7dWgPWfkaYh9BE0j6xTrAZ
+CxP8v0Swgha7b2AVNqxf5jxAJ7xNGNY/jdzeiB9Cp5ShrFGHFGmzCYUSe2hvyBX4
+9cl9W6nKSflG+lFfcmp2wcynk/aRO0H5ieXw3eD+3SB9snAWEZzDHfUj2ifTbzPD
+80Yd2mWz9pe1xyqxgnWQkAOIWUxWpECFz8wjA9U3257gEVgfN21Ng/vaVbxa1R4Z
+2A+bLjt0jgdXw0XX69FDolko3cWuiWfJNbxsrfSCRYwFUxNVxK9rtm5padL/kZ8W
+l9icSUSiIoEfXj1iDh4=
+=2Azi
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/crypto/test/data/tls/README.txt b/crypto/test/data/tls/README.txt
new file mode 100644
index 000000000..2f456ed68
--- /dev/null
+++ b/crypto/test/data/tls/README.txt
@@ -0,0 +1,8 @@
+The key and certificate .pem files here were generated using GnuTLS certtool and the accompanying template files:
+
+    certtool --generate-privkey > x509-ca-key.pem
+    certtool --generate-privkey > x509-client-key.pem
+    certtool --generate-privkey > x509-server-key.pem
+    certtool --generate-self-signed --load-privkey x509-ca-key.pem --template ca.tmpl --outfile x509-ca.pem
+    certtool --generate-certificate --load-privkey x509-client-key.pem --load-ca-certificate x509-ca.pem --load-ca-privkey x509-ca-key.pem --template client.tmpl --outfile x509-client.pem
+    certtool --generate-certificate --load-privkey x509-server-key.pem --load-ca-certificate x509-ca.pem --load-ca-privkey x509-ca-key.pem --template server.tmpl --outfile x509-server.pem
diff --git a/crypto/test/data/tls/ca.tmpl b/crypto/test/data/tls/ca.tmpl
new file mode 100644
index 000000000..44319d2de
--- /dev/null
+++ b/crypto/test/data/tls/ca.tmpl
@@ -0,0 +1,4 @@
+cn = BouncyCastle TLS Test CA
+ca
+cert_signing_key
+expiration_days = 7300
diff --git a/crypto/test/data/tls/client.tmpl b/crypto/test/data/tls/client.tmpl
new file mode 100644
index 000000000..f7f7e08a5
--- /dev/null
+++ b/crypto/test/data/tls/client.tmpl
@@ -0,0 +1,5 @@
+cn = BouncyCastle Test Client
+tls_www_client
+encryption_key
+signing_key
+expiration_days = 7300
diff --git a/crypto/test/data/tls/server.tmpl b/crypto/test/data/tls/server.tmpl
new file mode 100644
index 000000000..e406c48b4
--- /dev/null
+++ b/crypto/test/data/tls/server.tmpl
@@ -0,0 +1,5 @@
+cn = BouncyCastle Test Server
+tls_www_server
+encryption_key
+signing_key
+expiration_days = 7300
diff --git a/crypto/test/data/tls/x509-ca-key.pem b/crypto/test/data/tls/x509-ca-key.pem
new file mode 100644
index 000000000..b9144d2ba
--- /dev/null
+++ b/crypto/test/data/tls/x509-ca-key.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIFegIBAAKCATEAzIc3nB+Rux7seSPAeH7Q2CkHwRKRgZRQdmL259ySG7RKZ2N7
+rSaJFhnW02OaF5/GpkkWRT/WSZQQz6wMqKf/1H7idANZ9sA4hR1HDzHNQ4Ou7h+O
+Hxom+fbZzleGtMOMRdfBVYDx/vtlcv2WP1Y0gCHzHVN5854dae+OHbBI06o+HriY
+3MvQgr6VuwNItGbl87ZJDWb3FZ7JAq77hyrIQGaD6HbSFtRuunr2UAhkCCRy8gEg
+In0BXtXPFZSfFOGec0HiXtug4zEReSK2CVpeNyBWKw3xQuMfvRJ1SnRNoPeQ/Zk5
+FgMF/P8uXlszwY+7mri3XN+gj3yXJ+kNLIRsTG5/jo9v5qxfHKvYVwXtHLkSx/Be
+5OsBsc1c8eMIszMrAiiaF2FZxMPQD97kmalzQwIDAQABAoIBMCCrbJ+R5LD+Cd8j
+wye8IgHqiTmMN2p6VWFDrOrGWcmhF81sn6G907c7CLdFY/oSuc7FQpBsglRzGT2t
+iWwbQbXLUBggkkCO3bhYP98ry6eAI54Xs5eWPCmWBkkmbqGgfAnf98V/o6gLYgi8
+fEEWX6vOlcsrvVbDp9vMZGgA85u+cs5sxXC8rEwQw1FW/0hfCKt62i+7hbY8bgCK
+AuWV3y4B8gLxMNybvTKzeUcVhG2Bs8ZzdfP9auVILEsp6CjoqlW1QPsOJ6cJSVek
+3P7k8UyY++IpajMJZ0RxD5+MgqNvrU+F9fdk0W+z8ZW/FFncmtBS/xLAxg9lCicR
+qoiRGy4y/t8vKqtbMmqgev2JxSYYbE7AXD0Pp51hTRKAFASo6VdPh2/LBdXEU3uw
+4cXszskCgZkA3rdK8gg8/wMXjznff/Z3a6K0c5KPGGd+SK/Q4KwND6C0ca9JouVv
+PZ4qbqT++Ye1a5R3S5BSwCx13pNVmVicQQ4xQTCedaaTyhcNu3sClrK5uM4F1G7g
+p5RmUFPZFWODrFpqF39wwjGaUY229+JDI++c3hUfoSNQkLqKwwu0Z4aH/0UIDMy2
+5AbX4/QMvOdosTPDDBa/9rcCgZkA6xgXtiGl4PJ7bBCAtpEZ436qFjJOnhrBoxoP
+iK9wtp5alTrGYj9a0omia2Eg+JVDR2CkSsb5/98phaR0JY9dLFrk60a02YUhQAL8
+unkD0mBBRTuKejl/I31t2OTzUZOqfm7ggL6Jwy4BUkPmQbcKJg9CRs7vjF3lGmBd
+EPGQi+Dj1FnBMBnqWQ5zP2kjMNQ0uHWaHlWTO9UCgZgBnTx60pp2krQqApZfHA8z
+hYNfTxGgcKeWqUePSU/y7AxCwq1688TBopLWKHX8owIqnHHc51fiMrBMA69cJCtF
+wW+T9GFBowpxLYeY80RKiVMVRtD+ACu6qzWuoVzybb03k5QvRWowziE7NBa+ZzJr
+YUI2zdpj1Ziw49k7nqsZEP2NWRe82AL/VhlceplZCShWGHTycnvDswKBmCNkKx2m
+DxzAJEhua5IQYf9XcC+LPz6Z9JCjObdwAd3cFPLmODtOIlQTmDnmE1qYzdoO+Gyx
+a61TYSLXUQzeej5VKKUqrcsZOZozWOyRjzu6ddkAT6Z6xWMIXOMMBH1BZ6dE9dMr
+2/1gDZ7ezekSrxpvraCPQoy1Depcm2YTl5kXL/Ul1elx3U+u1zaykzOknpMuURdD
+9rhJAoGYX90aFh+SHKIc0nLBPCZA1dvvB6Nvwvynyr6pHDYFNZvsNBAqYQpLYgdY
+QRiGIHeUJgdDfuQADaTQy0t7fWuWE1orvK9ywrdXCo9KlstMmSInZ5LlF54wtgpw
+ivt7YjT1ewzxBJz67NdDVDLrOAo32ZTJ/iKUHoqXZGcCCWAtENzv9vRvcSJLIMYt
+bUtLF3QhyUCj7/ASc+Q=
+-----END RSA PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-ca.pem b/crypto/test/data/tls/x509-ca.pem
new file mode 100644
index 000000000..1639512b8
--- /dev/null
+++ b/crypto/test/data/tls/x509-ca.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAh+gAwIBAgIEUqKcyzANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhC
+b3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwHhcNMTMxMjA3MDM1ODAzWhcNMzMxMjAy
+MDM1ODAzWjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwggFS
+MA0GCSqGSIb3DQEBAQUAA4IBPwAwggE6AoIBMQDMhzecH5G7Hux5I8B4ftDYKQfB
+EpGBlFB2Yvbn3JIbtEpnY3utJokWGdbTY5oXn8amSRZFP9ZJlBDPrAyop//UfuJ0
+A1n2wDiFHUcPMc1Dg67uH44fGib59tnOV4a0w4xF18FVgPH++2Vy/ZY/VjSAIfMd
+U3nznh1p744dsEjTqj4euJjcy9CCvpW7A0i0ZuXztkkNZvcVnskCrvuHKshAZoPo
+dtIW1G66evZQCGQIJHLyASAifQFe1c8VlJ8U4Z5zQeJe26DjMRF5IrYJWl43IFYr
+DfFC4x+9EnVKdE2g95D9mTkWAwX8/y5eWzPBj7uauLdc36CPfJcn6Q0shGxMbn+O
+j2/mrF8cq9hXBe0cuRLH8F7k6wGxzVzx4wizMysCKJoXYVnEw9AP3uSZqXNDAgMB
+AAGjQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcEADAdBgNVHQ4E
+FgQU9mOvr6Pi7+4O5bKxRihqeCkyHukwDQYJKoZIhvcNAQELBQADggExAKyMiFmj
+YxzjXpQBD5dvRI7xZn79vH3lo13XRBXj/sbPXDXWIv21iLfutXn/RGGsq8piPXHG
+5UY3cpZR6gOq6QO1dJ91K0ViAJBFQdkhhtfbhqGY4jvj0vGO6zenG/WrjH26nCT7
+8S4L6ZoF6Y0EfQXluP50vEitTaZ6x/rung9h2JQ8rYKiRRVCA+tgBWK/CNhQ9LXy
+k3GU0mKLik0AkEFS17C0NWePIPEs/Kxv9iTEFacAN9wVHjZcMYnYtWaPNX0LWV8s
+2V2DMJxrmgCEcoXgJxlyEmvyqwpjB+2AiIQVIuWcwPqgBQoKHThT2zJcXV+bMhMs
+6cGvaIdvPxttduQsP349GcmUIlV6zFJq+HcMjfa8hZNIkuGBpUzdRQnu1+vYTkwz
+eVOPEIBZLzg9e2k=
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-client-dsa.pem b/crypto/test/data/tls/x509-client-dsa.pem
new file mode 100644
index 000000000..91d9e4415
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-dsa.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFmzCCBFOgAwIBAgIMVOBtHi6d3lDIPyoMMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5NTU0
+MloYDzIwMzUwMjEwMDk1NTQyWjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBDbGllbnQwggNHMIICOgYHKoZIzjgEATCCAi0CggEBAPvZZVsYbqUzF5Aq5JCU
+GJtg9+Hgrab/2F8vnEKsYKWtKFY4BCalIbhbZ/e4VPAEV1SlV/GgmtkPVrOutThq
+03j/eo98t82vismks6SMupNoyOiPqDZ+HyNEZFIQV9Ur/1D/tPBQGrD7Fbh5JOmK
+QPCoo46t9Xclfm+8u8bUIhFQV4LIkug5YBm2J8wy5FeaRYno1+4Hp543oD6SkOtF
+aJCp05nfiNRtaZy/LXr22UnSggG9M/zug+u3Bi2R70TI5FJq7TwYHfAlbesbLBjH
+QHvE95WlFxK1YovmziBLfew5RClZ3kgj7f3SL/3WeRy2ittQaObwV27ZNj4enYAD
+qkkCIQDsN/jM9mS5UDmPNRR6Wby2jvJQbHdm6yMqlFiIXVAECQKCAQEAzVrnDdM6
+kPTVALWJNYC279xIEMETimJGP39o3YXMJFlyoEzFYamJPneYlYpY4jGfuetC+jFn
+HouDivtjdf/vAc+og4jaro4NcaDCjzPZBTQuvb0cFhlSbVZsit1IMv/sztJUdPcd
+QKnGP+NfiChBDh8LLskvuVefEia0Ix/xwdK764zVn9oxiP5E2sKMMYnnFoVykbKU
+W43sOONe+P2zOF3VTIOUngszcNDKJsXrFKZ+MtE9FvKduaXBmd0y9Pat7jFCodry
+127mBeIZOKb9h3YMZCf7tQBFpX6o8JX4Ltw9hVRIsspgarli3K18WmOR+6pvAzJx
+IxxiYDbo1PHMAgOCAQUAAoIBAGPk3p3hPR51sTwf2LwDrZ5Fh9tLzFoSh6ojjiR/
+FJsqwbrEEPYi4vozJOBao17qfZayMArcq7QB5eUzBaeeggvyYgYPUIUh8kBZqeLp
+WtWYBXwDqNQiRSAqX0nl35SZEnyyou4kidHFW4SWSbTQw9pkM1gtP2Q7pkxabdjt
+fQTJsilslKFhoV/zWYsa72YJ1GilnDLwPanaBDdpnX84W+TDS2Gy2wb0o15etOTU
+Ly9EoHisZZfdwdIoAd0pJbjSHAJnudB+skL2VfPwfwxQDOTV7gh6XIe5h5bLKR3x
+ruLjNx1ppcTkjKmUruTp7V+/sH9EAS8B9epiNDGwL0s8gX6jdjB0MAwGA1UdEwEB
+/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAweAADAdBgNV
+HQ4EFgQUGvXFed+UN0C5+YrVvcR+7cn9U6swHwYDVR0jBBgwFoAU9mOvr6Pi7+4O
+5bKxRihqeCkyHukwDQYJKoZIhvcNAQELBQADggExAChl15lXRB43Oh+qIeibolYt
+OBCHlUQcQcFzqK556TKNQfhWoRv1ovxbxgPokogfnLfxKdmHrb4E+aF0syxNpXGn
+gBY2G7uyuKjoJnMNsxdTiS6jzps+LlgwHMYsFac0vcJATVz1U8hoR90B4pwkNkXv
+ErVXj4WvHqB5a4gsqPpAslZvhxOgWLvjA+1V4HohspnMVTup3zG6frdmwLWZ+FPI
+LyrEaUaWiilFl83RWuVlExhxGyfLF8u/06OJYHCUNtHT4UQavFzQMz5UwruGor7x
+n8QEE84SXaQxIyWO5T2vnlxZ0+fw/0GXyJS0F0WvJ5WkK+s+y6nXZ8Hq2fDecDWn
+1nw44LpnwGb//010RdtOeAHAQmGuuEf8rf1Xiw990hAYvTzM4cT9/l22vQPR3rg=
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-client-ecdsa.pem b/crypto/test/data/tls/x509-client-ecdsa.pem
new file mode 100644
index 000000000..fd8bc845b
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-ecdsa.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqzCCAWOgAwIBAgIMVOBhzQE9XhiRAUXpMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5MDcy
+NVoYDzIwMzUwMjEwMDkwNzI1WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBDbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASWdahP6F9xwnD6eABg
+2AwifyDDCw5jy5hGe+Lnvyjok/zdkw2n8lWCFdhInFC7mRcGS7aduF7dykv9C9oU
+mY17o3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1Ud
+DwEB/wQFAwMHgAAwHQYDVR0OBBYEFAg1z6i+u89U0RQrH9ApFWIklZrpMB8GA1Ud
+IwQYMBaAFPZjr6+j4u/uDuWysUYoangpMh7pMA0GCSqGSIb3DQEBCwUAA4IBMQCL
+RJIeoOlVOcseShihLd3OU7NU3tGeLDnEcwPOAPYsDN1c7V/zROTD/0W0nhSVdRfB
+AyxjjDjq3J/MTf1DrouEynoAU8SZL5OnxbceX74U2WsSaJU0PAaw9zTOaKoOuTET
+EVs/OMx00XXhZcu+lPFAKT3YNJKO1Rm7Sy4vnY4jnsuRsk9+D6E3CmDSnKQC/g90
+/0djrDTYpL9Lyd5zzekZPpzX7YgNfmq2X04521+3ahfq4bBMC8GRdAuon2h2FwFX
++aH9cjHHDgxPt5pHkg3Uk+Cl6KhVb2ilS0uYd4XaMPXOUOUOxRpmQ3/7YhNgyyaL
+IOP+fWkbOiFbUTDaOcbBo9y8gvy7+sXuGF5k4nHXNs46Ew1gThQPFtZjqgdiEi+A
+5hYd5P2j0V9L9LprzM79
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-client-key-dsa.pem b/crypto/test/data/tls/x509-client-key-dsa.pem
new file mode 100644
index 000000000..e4e9632c7
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-key-dsa.pem
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICZQIBADCCAjoGByqGSM44BAEwggItAoIBAQD72WVbGG6lMxeQKuSQlBibYPfh
+4K2m/9hfL5xCrGClrShWOAQmpSG4W2f3uFTwBFdUpVfxoJrZD1azrrU4atN4/3qP
+fLfNr4rJpLOkjLqTaMjoj6g2fh8jRGRSEFfVK/9Q/7TwUBqw+xW4eSTpikDwqKOO
+rfV3JX5vvLvG1CIRUFeCyJLoOWAZtifMMuRXmkWJ6NfuB6eeN6A+kpDrRWiQqdOZ
+34jUbWmcvy169tlJ0oIBvTP87oPrtwYtke9EyORSau08GB3wJW3rGywYx0B7xPeV
+pRcStWKL5s4gS33sOUQpWd5II+390i/91nkctorbUGjm8Fdu2TY+Hp2AA6pJAiEA
+7Df4zPZkuVA5jzUUelm8to7yUGx3ZusjKpRYiF1QBAkCggEBAM1a5w3TOpD01QC1
+iTWAtu/cSBDBE4piRj9/aN2FzCRZcqBMxWGpiT53mJWKWOIxn7nrQvoxZx6Lg4r7
+Y3X/7wHPqIOI2q6ODXGgwo8z2QU0Lr29HBYZUm1WbIrdSDL/7M7SVHT3HUCpxj/j
+X4goQQ4fCy7JL7lXnxImtCMf8cHSu+uM1Z/aMYj+RNrCjDGJ5xaFcpGylFuN7Djj
+Xvj9szhd1UyDlJ4LM3DQyibF6xSmfjLRPRbynbmlwZndMvT2re4xQqHa8tdu5gXi
+GTim/Yd2DGQn+7UARaV+qPCV+C7cPYVUSLLKYGq5YtytfFpjkfuqbwMycSMcYmA2
+6NTxzAIEIgIgC6eNrpVR96umhuvIbLz6sy2YwlGFpkK+LLJOL2GmQ4g=
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-client-key-ecdsa.pem b/crypto/test/data/tls/x509-client-key-ecdsa.pem
new file mode 100644
index 000000000..89232e303
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-key-ecdsa.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhANLHM3ugk0ZTEYNx
+uKn3ytAv71M61ChZAjWdC9nW1U7zoAoGCCqGSM49AwEHoUQDQgAElnWoT+hfccJw
++ngAYNgMIn8gwwsOY8uYRnvi578o6JP83ZMNp/JVghXYSJxQu5kXBku2nbhe3cpL
+/QvaFJmNew==
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-client-key.pem b/crypto/test/data/tls/x509-client-key.pem
new file mode 100644
index 000000000..386711f9a
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-key.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIFewIBAAKCATEA2HMtdpNQ+bHX26BW/haQL42XQKpZb71KRCtUnYVZbYNTriRc
+t9NIs+Ifm7clUH/hLTNE3xYHoBhG/quXl1NwJT7rq4vBZ2m2iHhV3GkV8mhfbIAn
+7Uw64ehw+US/CvvNccX8gA9aCjNqRdqXEcWGwKRoW0D0kEoSXzctCqdo5h0mwcPR
++DVVMwQiFDXS+LkqhL3eF2WsebQhuFiMNi+QShO6vgWIelGvj7kjmEVttEOsRDUx
+HAVtv7kMjmsi7QAyesLmyyFt7RF6BLYZZvVbjyz+Augr+MXmY2noH/ExkKfrhaLv
+dql3qEA1xKNFxi7LLJ8BU1P9jJ1So7nYX4P8Cely6oq/Gee1bPdhLK61K+R/wf9i
+L70krkxud7hmIlmvf0Ma0dHH3HM6dvjYaZ5/JwIDAQABAoIBMCdNzuEbwhX9Tcmj
+VXihJ0CDUQZciI4KTNyRE7Xg24rP0JesIav9sVN8yamQebWi6wQ2rZhm2y6eifG/
+vfCsJY6V3NyTLaT2JL+mPk0eRe51NnKw2M81yvFvsGgLLDe13qn8viuQ3hRBHtid
+Z1ZCTIRZtltvsZkr4icpEURAfwNeIsYHpW6RXK29X+TUm0Ev+oENKzGLdGTK06Xk
+6RRV7a34BIdg+7qVWL4ROzKPHKIrHw/Jmg+775UekvkGfos8Cd2eseHJROhAIje1
+WQTvDzOfx3PiUW9RW9/CKUXNeRtWJVhUG8lfqdKejn1+8CxPzwqgNRJbpGvCFa0R
+oDQ7+3l9ELdcA+sv9Qp2OFXIhagzXlGcC4SwMQrBiz6oK5dDqb7VEjE2igOGrRUZ
+i+7G3lECgZkA2qztgn6CbUhvLEXtiUr1EwX6ZHiB1ZVWjGo8cGmS2wI4/z48Ogln
+IvxnfAF7y9PYdHuNFkwbzFVRRdToLSw0bjkZVVzRodEJexvsK8LJQu2LycKOPO0n
+TLuWmr030wuZyU8ebrfuvdM7qDSSzrUc28+BUkPGiWAQ4lybqppoXtgXFeOLrDvn
+vp4OTkJmphM4eOSn8aPQSHECgZkA/WUAmT+/l+y6q6xo8WqEbvIkVzSfrSG3oXCd
+V40oNhEPHRRDWugq19mkBOMdFWEabAKaZTl1lLfijvs0fN2RIcARVAYrg2IlJvj4
+20/muERs3yM7tX7MgFCAdApgTGBToDy2O2ANdOhgG9KCcmf1D9ZKGnum7WhQHQaD
+3RV+S9LRhVyiIbeQzJxv8ejAaQVJPe99vez+TRcCgZgkLRuVzQwSvDr4HhFv9yCY
+JxMHcBA6n1wUGrco+a474SBAybD5APk5BnywPSaXz9ItYwsyNyEaKrspTFGkt31t
+BrE0OAjONmDVJwdpLe5Rzi6kEDWryqgHv7jonIkRtweYECi+tFsguENUTm5DGB9Q
+FgIU8/VyYJwqdpuiG61Mk38uNdizg62REWDYfY+xxdg/18QY67rXYQKBmGG/UaDM
+T5tKjaPlyUG/hkDKFayyNxpxVEXpjDiW0jkxEXR1OZpazxQe97+O4Mw7fhbGVkrQ
+BISO/s2LJ+83BWJQlh6klqqC9LP4/P6U7vqMIrdc/w5/UGH7K+IXkphD3F6GrQFw
+hyBe95wj84Awi/9E+acHphgU0jqwJbPmM6cKNxrL5hs9lZToj+Jwl7CrglprBUV7
+o4mXAoGZAJZ5BBXWeXf161K/n1hQY9dFLSYHjgv4sZMkDqyNhDpdW9HqJ2XEuscG
+Gf7vaCMEV+iOHFKYBS6XMcRXg+R7VOPRvPBNwQXf1ImcQzTVlerWHwMW+y0NAubd
+IIzAXPMzjcOBBiac2wg3cvBTQE29XJ/hy4Rk6qz/oWaIinYStCelCr1FR/Vw5HzE
+/SYmOzdAkWM9bv3Mp/g2
+-----END RSA PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-client.pem b/crypto/test/data/tls/x509-client.pem
new file mode 100644
index 000000000..4f2341995
--- /dev/null
+++ b/crypto/test/data/tls/x509-client.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDmjCCAlKgAwIBAgIEUqKc2jANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhC
+b3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwHhcNMTMxMjA3MDM1ODE4WhcNMzMxMjAy
+MDM1ODE4WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVzdCBDbGllbnQwggFS
+MA0GCSqGSIb3DQEBAQUAA4IBPwAwggE6AoIBMQDYcy12k1D5sdfboFb+FpAvjZdA
+qllvvUpEK1SdhVltg1OuJFy300iz4h+btyVQf+EtM0TfFgegGEb+q5eXU3AlPuur
+i8FnabaIeFXcaRXyaF9sgCftTDrh6HD5RL8K+81xxfyAD1oKM2pF2pcRxYbApGhb
+QPSQShJfNy0Kp2jmHSbBw9H4NVUzBCIUNdL4uSqEvd4XZax5tCG4WIw2L5BKE7q+
+BYh6Ua+PuSOYRW20Q6xENTEcBW2/uQyOayLtADJ6wubLIW3tEXoEthlm9VuPLP4C
+6Cv4xeZjaegf8TGQp+uFou92qXeoQDXEo0XGLsssnwFTU/2MnVKjudhfg/wJ6XLq
+ir8Z57Vs92EsrrUr5H/B/2IvvSSuTG53uGYiWa9/QxrR0cfcczp2+Nhpnn8nAgMB
+AAGjdjB0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0P
+AQH/BAUDAwegADAdBgNVHQ4EFgQU+vZUEQX5t+SGCe44YgvMmiMm8NkwHwYDVR0j
+BBgwFoAU9mOvr6Pi7+4O5bKxRihqeCkyHukwDQYJKoZIhvcNAQELBQADggExAI17
+s7teVt0hE9Zp40++AkK1k3/z7CCFsg+yS8+1VgFx5mU5XgNZMMgaASo1PCd9ZxFF
+QPXJ18MT4YjI6kWIC5RZspsTCwCq7jyg8bw36MHTNFUTYgfgMbhWPrUHRuOq0iG4
++wJVR6lXuHz/yaqMVNiy4yxBero7+zYiPZ+PKjEcvQxfcG+dIgptR6plNkFeZipT
+VCsSrit6iuStqmR7Jpj3nxPkR+g8aAfPCeiCxrSLNJFBj5J5AGSfkBzgjF5IE/z+
+tOgcmlFwtwOAIdRn/FUr7g1FFZh+Wy7AiB7qU4EcoW1HpcXJuRuKDrTzFH7L88zu
+nrv8n9XJSMDxdnP6kNwIv89HmYJWxtToO68PsATiGA6DiUUPk+FqIHqP0kgXXMSc
+ODFGSlXqh4ocvtA4APE=
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-server-dsa.pem b/crypto/test/data/tls/x509-server-dsa.pem
new file mode 100644
index 000000000..1078dbcbc
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-dsa.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFmjCCBFKgAwIBAgIMVOBtTzLTG8B9QCZvMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5NTYz
+MVoYDzIwMzUwMjEwMDk1NjMxWjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBTZXJ2ZXIwggNGMIICOQYHKoZIzjgEATCCAiwCggEBANblGd/puZYWDvmSMCx0
+AnQQcbmjUhB3MN68/fhkKFwBPU1QhJRiSEVDG1bnsUP55EQKDs1vpiSXgy89P3aT
+cagq0M+urrN97MPeKoBF8sk4o6axruspxD41OyKhW68Nnk54E+3mEPABS4yYpsuE
+hFfaZhOUXdA9aUFWnPN7oo7QlI+F7la8bluz8G8j81aMsEwjBdEKg+2Mp1+33NVa
+IWEegnunVEkb0vJd/WwLZYj+OgmK4fn/qqIdRXIbjeNUvpAtklE5NrARswI0RXuX
+yWxPfBrQSbWk3DQvbuWbEeSUrhYY67YbpPGA5Rng8awpxdXhvzsSmT9c6BlrLfF6
+IN0CIQDWtWHaMmlAVZW9p0Dmsj70sNHJxOYWlqjCEzF7axV3fwKCAQAgFZojuX/n
+leri2E9C6b8cQTof46JZeAeG2Svk8I91cnIv0iXTHfyzWLWDAYnfeuMmt+nMA6/A
+1K94YI1duc3iahLa5U3fOACWJdFtL/CAFVyNth7lDeHbWTFjM5HgA76pzmAfvFtZ
+pcJp1AUZZB+3coKSr6ZrMagt1HhoefGyo7JkPpsxGiFEKB79rXkFi0AEsSnbduFE
+czrg8DBdXm9//yUQnTw2pphcwLZt6t6Eq3Wrpu364Dn+hJegpLfA6uOhtRJw+7xv
+5jkBbvIxp1PLZEn16qmPKMs3Im4QUa2u5F0nLYUGHi01i5OSn0UWnom8EcDis/0Q
+CrkyBaiMaNkkA4IBBQACggEAQob2cpVeV4uwnX8XutQLro6BkEFMhpaQLUzbI97J
+Nh1OUMnW3K2KCCdhygTYfH3j2S8ys4W30zutHyhiApUdYUZKxNXXu4+NPoLwCyAE
+rrN5db5QgVx8aOG/Gfmp4POMpeGKioXgOsSWpUB0uyedgT4sszq/vuZc455HiS07
+LTlaJjeIQFtKdVYXGKNWqP/WLRnjSY9IXA5hxBXvlNAVGS/M4TvBWURUJF33THTq
+ioS0DYJ9Ikyn2fsgLgSRRyNU7Y5945yUhY5p6E6Njk0ZBB+2YUtB6TQhPvEY/G48
+xwH4hTZppmmfu2UIE+YlsWv10buTOT+mxU/5LjDI6RQM2KN2MHQwDAYDVR0TAQH/
+BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHQ8BAf8EBQMDB4AAMB0GA1Ud
+DgQWBBS8bllTQWk2fm2R6XSZEIoRTd6/+zAfBgNVHSMEGDAWgBT2Y6+vo+Lv7g7l
+srFGKGp4KTIe6TANBgkqhkiG9w0BAQsFAAOCATEAptRUkWUT+MdvD7v733B6Gad+
+wO0G9n7Au7ZsS6NVPxc4DrozFyyhPU1uOl1koUyYQP52V1lkm1+rS7tQXm4+32iz
+bLwDCYn4LD1IZQ2hTNY/g/U0oA1dp3ZOJeydZDZ9Bm1NWYeWqfHk/ZiHMdrEdIUo
+7Zkg1T0ZBvmBXTUJzeDqIV0fMotFOQBJg5avyCcX56jHXbu2AmGQsQf6NoniXG37
+wY/xiPSUUmOKjAU/B4tc24ADa5r37JzlHbpOGe1nTb8AYxjyPDdjO4Um2BugKE+K
+rq+NJMg8Ej2sXLOnS2FweCoIR+RXGAUUBizlHMKJ2UGQ5sCX7U86qvniMcO8CsXR
+7eSkBTYpxVLENxb2zf3g22972QWuL9eHyQfuO5dca2JnenZv1aIrhTH59fX8Sw==
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-server-ecdsa.pem b/crypto/test/data/tls/x509-server-ecdsa.pem
new file mode 100644
index 000000000..221fce99a
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-ecdsa.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqzCCAWOgAwIBAgIMVOBhgRR/e3iAhKcbMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5MDYw
+OVoYDzIwMzUwMjEwMDkwNjA5WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBTZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQzME1njDHqizStYiqf
+xC45McfdqtcDFCsyik7g7JwwGBzDI3WTP5stb8gLHwGARqkaTIDr/wau+N12bIxp
+Tj6Co3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
+DwEB/wQFAwMHgAAwHQYDVR0OBBYEFOaifCD845nra+asxN59LzV1LlkxMB8GA1Ud
+IwQYMBaAFPZjr6+j4u/uDuWysUYoangpMh7pMA0GCSqGSIb3DQEBCwUAA4IBMQCc
+wMTPTd/82sDeJkN6B8M2AFfLf/0uw01/2I/UQp2rJCJu1Yps9S1foHz939o4qJA3
++kieb3eVE8rcMQHMv42hagj09LmYY1Unzdu8xJDMq7gDIph72Fjy5YFqb9je79C4
+JPOadGsAsG9OY9DxTKsHyiX3Mqztx1hUQ0mNiAyaFEDGus+ZhSGMdHj0uR1LqZL9
+9OgszZY0UomZfhwnPg2sHumdh+13RZ38w33b43LpCcyu7v05szquqJ8lnol8j10r
+6sH1xXZmb6OJIXZeHTnX5Dv1z0/R8qyG3gyVU7sP+8KVk529Q/YjIJ8YEV1LSERW
+9cKbI+8DMxVE/z01gd0YLqpPcDSfzGUTww+lMWyGQ+KDUD2bC2O4LbZhzT+ZxCPN
++mw+T5KV78wcG02PThMf
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-server-key-dsa.pem b/crypto/test/data/tls/x509-server-key-dsa.pem
new file mode 100644
index 000000000..d6073b31f
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-key-dsa.pem
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQDW5Rnf6bmWFg75kjAsdAJ0EHG5
+o1IQdzDevP34ZChcAT1NUISUYkhFQxtW57FD+eRECg7Nb6Ykl4MvPT92k3GoKtDP
+rq6zfezD3iqARfLJOKOmsa7rKcQ+NTsioVuvDZ5OeBPt5hDwAUuMmKbLhIRX2mYT
+lF3QPWlBVpzze6KO0JSPhe5WvG5bs/BvI/NWjLBMIwXRCoPtjKdft9zVWiFhHoJ7
+p1RJG9LyXf1sC2WI/joJiuH5/6qiHUVyG43jVL6QLZJROTawEbMCNEV7l8lsT3wa
+0Em1pNw0L27lmxHklK4WGOu2G6TxgOUZ4PGsKcXV4b87Epk/XOgZay3xeiDdAiEA
+1rVh2jJpQFWVvadA5rI+9LDRycTmFpaowhMxe2sVd38CggEAIBWaI7l/55Xq4thP
+Qum/HEE6H+OiWXgHhtkr5PCPdXJyL9Il0x38s1i1gwGJ33rjJrfpzAOvwNSveGCN
+XbnN4moS2uVN3zgAliXRbS/wgBVcjbYe5Q3h21kxYzOR4AO+qc5gH7xbWaXCadQF
+GWQft3KCkq+mazGoLdR4aHnxsqOyZD6bMRohRCge/a15BYtABLEp23bhRHM64PAw
+XV5vf/8lEJ08NqaYXMC2berehKt1q6bt+uA5/oSXoKS3wOrjobUScPu8b+Y5AW7y
+MadTy2RJ9eqpjyjLNyJuEFGtruRdJy2FBh4tNYuTkp9FFp6JvBHA4rP9EAq5MgWo
+jGjZJAQiAiBhYRIk49XUmvOqUx953A+n8/KWUfyZyTHPkcIXBvW+Ig==
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-server-key-ecdsa.pem b/crypto/test/data/tls/x509-server-key-ecdsa.pem
new file mode 100644
index 000000000..01fe38567
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-key-ecdsa.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhALoiXMjOXuPkNVT6
+g/8PnBmXj8wEvWXCyrpO+fh9EMTdoAoGCCqGSM49AwEHoUQDQgAEMzBNZ4wx6os0
+rWIqn8QuOTHH3arXAxQrMopO4OycMBgcwyN1kz+bLW/ICx8BgEapGkyA6/8Grvjd
+dmyMaU4+gg==
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-server-key.pem b/crypto/test/data/tls/x509-server-key.pem
new file mode 100644
index 000000000..894fbb999
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-key.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIFfAIBAAKCATEAsy/PN54P7ZkH23FHjtgZuzpyGa84+YZOM3snikem660o90Ws
+TquMMtXNeAQPGAFUpu2Qxo416ogolqqtTXYJz9qsp/eDvzlNCJN7HKb5Fg28HiCc
+dMx2AR03qlGkxI3WsCPxFsX83zrz8wZSvrO7jjS1/WfJW1vYme+kktYcg90+5pJ0
+5hEAIwFU/mh9KnVRVdnINCcCqdnwpA1fAPXLwqNYdRolaczaQFI1ml8YbOgCnefz
+uxeSt9qz/7TkBITVvBf3AH5fUG6UezNxma85o7b1P4QJNiWGA7uwh7iABpIR7795
+Fk8ebOSUD9lZ9iqo0CzXBDAiwJe3uzSLBITlF0xbyveH+ILj7sKpa0CkV+5yZ1Cm
+B426lluuBlYxbi2wfAgN8arfQtnTobl/m+ClrQIDAQABAoIBMAEZ6kKNFNbIeBqT
+24dTDIUsj+DCbGPsR86wsG5FOgFRa96K0qcAS53nBAXDynKFl6igJcXs4WfLiRnN
+QGcgob+cIgM6pT8rDnlZT/291pH9mD/MwnyHDwak7C7dGXrRojWQl1TGZqjJS0f5
+enaN5lUnXwROZqsxUYohFkmtj1dpjxTqUGZ0In1JdG2mDmunMgjkJ/UqtCp0QFCp
+xtd1u5/913RBFC+n/CJl1+TnNQ4WNDRtkHZfJpVp+dOldQr028CJU3C5u1JB9ped
+JK+dywUFlqH1Tdk2v5CQV5esvbFpXX7FIlHuId+JOwFyvkR5+7NmyQ2uAz/Yeg8K
+ItdRhNQCJpYPlPH9Yb1JooEhPYrQX6oz/q2R3qiFdtc1cMZW3KCtIALZk30lW/o+
+B4EiysECgZkA1cYcdgNKy5K1O/IOyhr1eYK2us738Z3A57gC1y+m58D02dzmwacg
+HKZI9Cz98L8yEyORkdxpg86xi+NIcdU15BXXioFgMI5ZSfNVBcO4rv8G/QCxEWDr
+gnXe7FYuLlOKBbVXx7fJ0IXRUVbUlH5ZqE2HlFbSdPJmlaKtbxxyT7xhhWkf/MuI
+hRn+MFvbXvC+JqL+FS141kECgZkA1pS74hc7H5blq0J51E8EDtgU5m770VlQKhbW
+Z1D0oXVfB19DD3SX4xu22/6XlKXLlQDx4ssxVw11VIkd9WhkIrqq4U644XL5xXmf
+PMsR+fG2o+Y7MY4TNy+4qcuOK17n6R2pxR4zQoVnZs/qL4s5jPKMsC76C4Udfxfh
+yup0eFEJ+jPQdWYWQ6uX3UF0rA5x0Tb200aKbG0CgZkAkwaoeHoXLR//yfTXOyWD
+g0jliGHkoabQEA681WcOsgJB5L1LcBETwuCS+G0hUj0NoaAq9FjVsTOtZPqyzqfH
+YtGq5rXIhFzDCFt1NHvCP4ljMwsQvVUdZSLQaVd0d6Q5H2fzsYa0JNiEeB7yIhcs
+btaz0tBL+ubkqzGxeuPjsvdrUyhUObd6c6DG9FeY7xlAjq43djVKEIECgZhITde9
+SDyo2UzMV1r72iAw7EimmPELSsADXqyiJZo4qXb64fOTyqK/aQBFwtTKxs8Bh076
+L6ORhLxrXsSUg7dyKFoaD0+mz/ovu1qXvolxIix7r8F0Yj5BUzgzJp7iKFmWqGMj
+Q5jcKl18PETZ/lzHDJexajLhHNqij6aKnFPgktX80+bDGEIaTUCf0kWBEGDzsUSc
+TmGoRQKBmQDVFQ6EAOXXNp2bMLZ78qnxmS+NZPijBDeVu1cXMYdRSBoldrtSHKUM
+NFjPfesz4IRbEePK5s0vgM88QCDaspy8aLj//gh9YuqijbHOfhvMUP6MqzU/jJN7
+MDAxcGbcoFqdWTP1HB9qeX91UNRwN+2/xfO6SrLbfTvG4v/sntr8YfVYya2EuAa9
+qLk0TB3QXaoHknsz7EhRnw==
+-----END RSA PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-server.pem b/crypto/test/data/tls/x509-server.pem
new file mode 100644
index 000000000..efc806967
--- /dev/null
+++ b/crypto/test/data/tls/x509-server.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDmjCCAlKgAwIBAgIEUqKc4DANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhC
+b3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwHhcNMTMxMjA3MDM1ODI0WhcNMzMxMjAy
+MDM1ODI0WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVzdCBTZXJ2ZXIwggFS
+MA0GCSqGSIb3DQEBAQUAA4IBPwAwggE6AoIBMQCzL883ng/tmQfbcUeO2Bm7OnIZ
+rzj5hk4zeyeKR6brrSj3RaxOq4wy1c14BA8YAVSm7ZDGjjXqiCiWqq1NdgnP2qyn
+94O/OU0Ik3scpvkWDbweIJx0zHYBHTeqUaTEjdawI/EWxfzfOvPzBlK+s7uONLX9
+Z8lbW9iZ76SS1hyD3T7mknTmEQAjAVT+aH0qdVFV2cg0JwKp2fCkDV8A9cvCo1h1
+GiVpzNpAUjWaXxhs6AKd5/O7F5K32rP/tOQEhNW8F/cAfl9QbpR7M3GZrzmjtvU/
+hAk2JYYDu7CHuIAGkhHvv3kWTx5s5JQP2Vn2KqjQLNcEMCLAl7e7NIsEhOUXTFvK
+94f4guPuwqlrQKRX7nJnUKYHjbqWW64GVjFuLbB8CA3xqt9C2dOhuX+b4KWtAgMB
+AAGjdjB0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0P
+AQH/BAUDAwegADAdBgNVHQ4EFgQUlILGBt8EzhYtlNrOPrhfwi0s6bkwHwYDVR0j
+BBgwFoAU9mOvr6Pi7+4O5bKxRihqeCkyHukwDQYJKoZIhvcNAQELBQADggExAMQd
+MLOlWKWJxh6IP7sRlWKjpWZyu43eSOfvEpVljW8VaRxJC8UdhpFxsXS6Ml7wEMUC
+BkVNHGMxho/GJMXUBV7OsQSv0et1o45bmkN+KKisSVReSgcj6Drp/BRcUcybPtcJ
+aDW1txh/suHWppVmtkIkZIF/3IR2qFekDdCLoluiEOvbNn3YjUnQLm6Eo0pBxgpb
+W5MF3/19UckP1sLrs5vFk1dtDBZ/agpI9I0psv+6OsjosvrdpjIPHjwmoZ+oYtKc
+4Q30vzLCVtGGyzXWBZ+Z6AbmZpJPDQtul522XKE2vE8GA3+X/RXVAZB8a86DWtzq
+J1O6D+KOyA9zwe1CO+VJ5fMkjSNXY6WDzEXqyKEBP8tkkvSByiM546CXtNDbEwBe
+PtYQf223mpK56XTFq4k=
+-----END CERTIFICATE-----
diff --git a/crypto/test/lib/nunit.core.dll b/crypto/test/lib/nunit.core.dll
index f58c07d42..8d99c637d 100644
--- a/crypto/test/lib/nunit.core.dll
+++ b/crypto/test/lib/nunit.core.dll
Binary files differdiff --git a/crypto/test/lib/nunit.core.interfaces.dll b/crypto/test/lib/nunit.core.interfaces.dll
index cdf50b687..70a76b21c 100644
--- a/crypto/test/lib/nunit.core.interfaces.dll
+++ b/crypto/test/lib/nunit.core.interfaces.dll
Binary files differdiff --git a/crypto/test/lib/nunit.framework.dll b/crypto/test/lib/nunit.framework.dll
index c7b1c65d9..ba484ba8c 100644
--- a/crypto/test/lib/nunit.framework.dll
+++ b/crypto/test/lib/nunit.framework.dll
Binary files differdiff --git a/crypto/test/src/asn1/test/AllTests.cs b/crypto/test/src/asn1/test/AllTests.cs
index a1ae8fd48..ad2f90362 100644
--- a/crypto/test/src/asn1/test/AllTests.cs
+++ b/crypto/test/src/asn1/test/AllTests.cs
@@ -9,24 +9,22 @@ namespace Org.BouncyCastle.Asn1.Tests
     {
         public static void Main(string[] args)
         {
-//            junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-		public static TestSuite suite()
+        [Suite]
+		public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("ASN.1 tests");
-
-			suite.Add(new AllTests());
-
-			// TODO Add these tests to RegressionTest list
-			suite.Add(new Asn1SequenceParserTest());
-			suite.Add(new OctetStringTest());
-			suite.Add(new ParseTest());
-			suite.Add(new TimeTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("ASN.1 tests");
+			    // TODO Add these tests to RegressionTest list
+			    suite.Add(new Asn1SequenceParserTest());
+			    suite.Add(new OctetStringTest());
+			    suite.Add(new ParseTest());
+			    suite.Add(new TimeTest());
+			    return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/cms/test/AllTests.cs b/crypto/test/src/cms/test/AllTests.cs
index 1ce3b7c8d..b7ac7644b 100644
--- a/crypto/test/src/cms/test/AllTests.cs
+++ b/crypto/test/src/cms/test/AllTests.cs
@@ -9,27 +9,26 @@ namespace Org.BouncyCastle.Cms.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-		public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("CMS Tests");
-
-			suite.Add(new CompressedDataTest());
-            suite.Add(new CompressedDataStreamTest());
-			suite.Add(new EnvelopedDataTest());
-			suite.Add(new EnvelopedDataStreamTest());
-			suite.Add(new Rfc4134Test());
-			suite.Add(new SignedDataTest());
-			suite.Add(new SignedDataStreamTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("CMS Tests");
+                suite.Add(new CompressedDataTest());
+                suite.Add(new CompressedDataStreamTest());
+                suite.Add(new EnvelopedDataTest());
+                suite.Add(new EnvelopedDataStreamTest());
+                suite.Add(new Rfc4134Test());
+                suite.Add(new SignedDataTest());
+                suite.Add(new SignedDataStreamTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/cms/test/CMSTestUtil.cs b/crypto/test/src/cms/test/CMSTestUtil.cs
index ef7f9169f..69f7c2983 100644
--- a/crypto/test/src/cms/test/CMSTestUtil.cs
+++ b/crypto/test/src/cms/test/CMSTestUtil.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.IO;
 using System.Text;
 
@@ -12,6 +13,7 @@ using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.IO;
 using Org.BouncyCastle.X509;
 using Org.BouncyCastle.X509.Extension;
+using Org.BouncyCastle.X509.Store;
 
 namespace Org.BouncyCastle.Cms.Tests
 {
@@ -450,7 +452,40 @@ namespace Org.BouncyCastle.Cms.Tests
 		*  INTERNAL METHODS
 		*
 		*/
-		private static AuthorityKeyIdentifier CreateAuthorityKeyId(
+        internal static IX509Store MakeAttrCertStore(params IX509AttributeCertificate[] attrCerts)
+        {
+            IList attrCertList = new ArrayList();
+            foreach (IX509AttributeCertificate attrCert in attrCerts)
+            {
+                attrCertList.Add(attrCert);
+            }
+
+            return X509StoreFactory.Create("AttributeCertificate/Collection", new X509CollectionStoreParameters(attrCertList));
+        }
+
+        internal static IX509Store MakeCertStore(params X509Certificate[] certs)
+        {
+            IList certList = new ArrayList();
+            foreach (X509Certificate cert in certs)
+            {
+                certList.Add(cert);
+            }
+
+            return X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(certList));
+        }
+
+        internal static IX509Store MakeCrlStore(params X509Crl[] crls)
+        {
+            IList crlList = new ArrayList();
+            foreach (X509Crl crl in crls)
+            {
+                crlList.Add(crl);
+            }
+
+            return X509StoreFactory.Create("CRL/Collection", new X509CollectionStoreParameters(crlList));
+        }
+
+        private static AuthorityKeyIdentifier CreateAuthorityKeyId(
 			AsymmetricKeyParameter _pubKey)
 		{
 			SubjectPublicKeyInfo _info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_pubKey);
diff --git a/crypto/test/src/cms/test/MiscDataStreamTest.cs b/crypto/test/src/cms/test/MiscDataStreamTest.cs
index f958d058f..4cb19884b 100644
--- a/crypto/test/src/cms/test/MiscDataStreamTest.cs
+++ b/crypto/test/src/cms/test/MiscDataStreamTest.cs
@@ -207,15 +207,12 @@ namespace Org.BouncyCastle.Cms.Tests
 
 			sp.GetSignedContent().Drain();
 
-			//
-			// compute expected content digest
-			//
-			IDigest md = DigestUtilities.GetDigest("SHA1");
-			byte[] cDataOutBytes = cDataOut.ToArray();
-			md.BlockUpdate(cDataOutBytes, 0, cDataOutBytes.Length);
-			byte[] hash = DigestUtilities.DoFinal(md);
-
-			VerifySignatures(sp, hash);
+            byte[] cDataOutBytes = cDataOut.ToArray();
+
+            // compute expected content digest
+            byte[] hash = DigestUtilities.CalculateDigest("SHA1", cDataOutBytes);
+
+            VerifySignatures(sp, hash);
 		}
 	}
 }
diff --git a/crypto/test/src/cms/test/SignedDataStreamTest.cs b/crypto/test/src/cms/test/SignedDataStreamTest.cs
index 96f3f125d..2131938e7 100644
--- a/crypto/test/src/cms/test/SignedDataStreamTest.cs
+++ b/crypto/test/src/cms/test/SignedDataStreamTest.cs
@@ -233,112 +233,74 @@ namespace Org.BouncyCastle.Cms.Tests
 			VerifySignatures(sp);
 		}
 
-//		[Test]
-//		public void TestSha1WithRsaNoAttributes()
-//		{
-//			IList certList = new ArrayList();
-//			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes(TestMessage));
-//
-//			certList.Add(OrigCert);
-//			certList.Add(SignCert);
-//
-//			IX509Store x509Certs = X509StoreFactory.Create(
-//				"Certificate/Collection",
-//				new X509CollectionStoreParameters(certList));
-//
-//			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
-//
-//			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-//
-//			gen.AddCertificates(x509Certs);
-//
-//			CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false, false);
-//
-//			CmsSignedDataParser sp = new CmsSignedDataParser(
-//				new CmsTypedStream(new MemoryStream(Encoding.ASCII.GetBytes(TestMessage), false)), s.GetEncoded());
-//
-//			sp.GetSignedContent().Drain();
-//
-//			//
-//			// compute expected content digest
-//			//
-//			IDigest md = DigestUtilities.GetDigest("SHA1");
-//
-//			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
-//			md.BlockUpdate(testBytes, 0, testBytes.Length);
-//			byte[] hash = DigestUtilities.DoFinal(md);
-//
-//			VerifySignatures(sp, hash);
-//		}
+        //[Test]
+        //public void TestSha1WithRsaNoAttributes()
+        //{
+        //    CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes(TestMessage));
 
-//		[Test]
-//		public void TestDsaNoAttributes()
-//		{
-//			IList certList = new ArrayList();
-//			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes(TestMessage));
-//
-//			certList.Add(OrigDsaCert);
-//			certList.Add(SignCert);
-//
-//			IX509Store x509Certs = X509StoreFactory.Create(
-//				"Certificate/Collection",
-//				new X509CollectionStoreParameters(certList));
-//
-//			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
-//
-//			gen.AddSigner(OrigDsaKP.Private, OrigDsaCert, CmsSignedDataGenerator.DigestSha1);
-//
-//			gen.AddCertificates(x509Certs);
-//
-//			CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false, false);
-//
-//			CmsSignedDataParser sp = new CmsSignedDataParser(
-//				new CmsTypedStream(
-//				new MemoryStream(Encoding.ASCII.GetBytes(TestMessage), false)),
-//				s.GetEncoded());
-//
-//			sp.GetSignedContent().Drain();
-//
-//			//
-//			// compute expected content digest
-//			//
-//			IDigest md = DigestUtilities.GetDigest("SHA1");
-//
-//			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
-//			md.BlockUpdate(testBytes, 0, testBytes.Length);
-//			byte[] hash = DigestUtilities.DoFinal(md);
-//
-//			VerifySignatures(sp, hash);
-//		}
+        //    IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
+
+        //    CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+        //    gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
+        //    gen.AddCertificates(x509Certs);
+
+        //    CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false, false);
+
+        //    CmsSignedDataParser sp = new CmsSignedDataParser(
+        //        new CmsTypedStream(new MemoryStream(Encoding.ASCII.GetBytes(TestMessage), false)), s.GetEncoded());
+
+        //    sp.GetSignedContent().Drain();
+
+        //    byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+
+        //    // compute expected content digest
+        //    byte[] hash = DigestUtilities.CalculateDigest("SHA1", testBytes);
+
+        //    VerifySignatures(sp, hash);
+        //}
+
+        //[Test]
+        //public void TestDsaNoAttributes()
+        //{
+        //    CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes(TestMessage));
+
+        //    IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigDsaCert, SignCert);
+
+        //    CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+        //    gen.AddSigner(OrigDsaKP.Private, OrigDsaCert, CmsSignedDataGenerator.DigestSha1);
+        //    gen.AddCertificates(x509Certs);
+
+        //    CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false, false);
+
+        //    CmsSignedDataParser sp = new CmsSignedDataParser(
+        //        new CmsTypedStream(
+        //        new MemoryStream(Encoding.ASCII.GetBytes(TestMessage), false)),
+        //        s.GetEncoded());
+
+        //    sp.GetSignedContent().Drain();
+
+        //    byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+
+        //    // compute expected content digest
+        //    byte[] hash = DigestUtilities.CalculateDigest("SHA1", testBytes);
+
+        //    VerifySignatures(sp, hash);
+        //}
 
 		[Test]
 		public void TestSha1WithRsa()
 		{
-			IList certList = new ArrayList();
-			IList crlList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			crlList.Add(SignCrl);
-			crlList.Add(OrigCrl);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-			IX509Store x509Crls = X509StoreFactory.Create(
-				"CRL/Collection",
-				new X509CollectionStoreParameters(crlList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
+            IX509Store x509Crls = CmsTestUtil.MakeCrlStore(SignCrl, OrigCrl);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 			gen.AddCrls(x509Crls);
 
-			Stream sigOut = gen.Open(bOut);
+            Stream sigOut = gen.Open(bOut);
 
 			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 			sigOut.Write(testBytes, 0, testBytes.Length);
@@ -352,41 +314,33 @@ namespace Org.BouncyCastle.Cms.Tests
 
 			sp.GetSignedContent().Drain();
 
-			//
-			// compute expected content digest
-			//
-			IDigest md = DigestUtilities.GetDigest("SHA1");
-			md.BlockUpdate(testBytes, 0, testBytes.Length);
-			byte[] hash = DigestUtilities.DoFinal(md);
+            // compute expected content digest
+            byte[] hash = DigestUtilities.CalculateDigest("SHA1", testBytes);
 
-			VerifySignatures(sp, hash);
+            VerifySignatures(sp, hash);
 
 			//
 			// try using existing signer
 			//
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigners(sp.GetSignerInfos());
-
 			gen.AddCertificates(sp.GetCertificates("Collection"));
 			gen.AddCrls(sp.GetCrls("Collection"));
 
-			bOut.SetLength(0);
-
-			sigOut = gen.Open(bOut, true);
+            bOut.SetLength(0);
 
+            sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			VerifyEncodedData(bOut);
+            VerifyEncodedData(bOut);
 
-			//
+            //
 			// look for the CRLs
 			//
 			ArrayList col = new ArrayList(x509Crls.GetMatches(null));
 
-			Assert.AreEqual(2, col.Count);
+            Assert.AreEqual(2, col.Count);
 			Assert.IsTrue(col.Contains(SignCrl));
 			Assert.IsTrue(col.Contains(OrigCrl));
 		}
@@ -394,80 +348,53 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSha1WithRsaNonData()
 		{
-			IList certList = new ArrayList();
-			IList crlList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			crlList.Add(SignCrl);
-			crlList.Add(OrigCrl);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-			IX509Store x509Crls = X509StoreFactory.Create(
-				"CRL/Collection",
-				new X509CollectionStoreParameters(crlList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
+            IX509Store x509Crls = CmsTestUtil.MakeCrlStore(SignCrl, OrigCrl);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 			gen.AddCrls(x509Crls);
 
-			Stream sigOut = gen.Open(bOut, "1.2.3.4", true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+            Stream sigOut = gen.Open(bOut, "1.2.3.4", true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
+            CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
-			CmsTypedStream stream = sp.GetSignedContent();
+            CmsTypedStream stream = sp.GetSignedContent();
 
 			Assert.AreEqual("1.2.3.4", stream.ContentType);
 
 			stream.Drain();
 
-			//
-			// compute expected content digest
-			//
-			IDigest md = DigestUtilities.GetDigest("SHA1");
-			md.BlockUpdate(testBytes, 0, testBytes.Length);
-			byte[] hash = DigestUtilities.DoFinal(md);
+            // compute expected content digest
+            byte[] hash = DigestUtilities.CalculateDigest("SHA1", testBytes);
 
-			VerifySignatures(sp, hash);
+            VerifySignatures(sp, hash);
 		}
 
-		[Test]
+        [Test]
 		public void TestSha1AndMD5WithRsa()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddDigests(CmsSignedDataStreamGenerator.DigestSha1,
 				CmsSignedDataStreamGenerator.DigestMD5);
 
-			Stream sigOut = gen.Open(bOut);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+            Stream sigOut = gen.Open(bOut);
 			sigOut.Write(testBytes, 0, testBytes.Length);
 
 			gen.AddCertificates(x509Certs);
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestMD5);
 
@@ -486,39 +413,29 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSha1WithRsaEncapsulatedBufferedStream()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
-			//
+            //
 			// find unbuffered length
 			//
 			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
 			Stream sigOut = gen.Open(bOut, true);
-
 			for (int i = 0; i != 2000; i++)
 			{
 				sigOut.WriteByte((byte)(i & 0xff));
 			}
-
 			sigOut.Close();
 
 			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
-			sp.GetSignedContent().Drain();
+            sp.GetSignedContent().Drain();
 
-			VerifySignatures(sp);
+            VerifySignatures(sp);
 
 			int unbufferedLength = bOut.ToArray().Length;
 
@@ -528,9 +445,7 @@ namespace Org.BouncyCastle.Cms.Tests
 			bOut.SetLength(0);
 
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
 			sigOut = gen.Open(bOut, true);
@@ -541,10 +456,10 @@ namespace Org.BouncyCastle.Cms.Tests
 				data[i] = (byte)(i & 0xff);
 			}
 
-			Streams.PipeAll(new MemoryStream(data, false), sigOut);
+            Streams.PipeAll(new MemoryStream(data, false), sigOut);
 			sigOut.Close();
 
-			VerifyEncodedData(bOut);
+            VerifyEncodedData(bOut);
 
 			Assert.AreEqual(unbufferedLength, bOut.ToArray().Length);
 		}
@@ -552,23 +467,15 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSha1WithRsaEncapsulatedBuffered()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
 			//
 			// find unbuffered length
 			//
 			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
 			Stream sigOut = gen.Open(bOut, true);
@@ -594,14 +501,11 @@ namespace Org.BouncyCastle.Cms.Tests
 			bOut.SetLength(0);
 
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.SetBufferSize(300);
-
-			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
+            gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
 			gen.AddCertificates(x509Certs);
 
-			sigOut = gen.Open(bOut, true);
+            sigOut = gen.Open(bOut, true);
 
 			for (int i = 0; i != 2000; i++)
 			{
@@ -618,38 +522,29 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSha1WithRsaEncapsulated()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut, true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+            Stream sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
+            CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
 			sp.GetSignedContent().Drain();
 
 			VerifySignatures(sp);
 
-			byte[] contentDigest = (byte[])gen.GetGeneratedDigests()[CmsSignedGenerator.DigestSha1];
+            byte[] contentDigest = (byte[])gen.GetGeneratedDigests()[CmsSignedGenerator.DigestSha1];
 
-			ArrayList signers = new ArrayList(sp.GetSignerInfos().GetSigners());
+            ArrayList signers = new ArrayList(sp.GetSignerInfos().GetSigners());
 
 			AttributeTable table = ((SignerInformation) signers[0]).SignedAttributes;
 			Asn1.Cms.Attribute hash = table[CmsAttributes.MessageDigest];
@@ -660,26 +555,22 @@ namespace Org.BouncyCastle.Cms.Tests
 			// try using existing signer
 			//
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigners(sp.GetSignerInfos());
-
 			gen.AddCertificates(sp.GetCertificates("Collection"));
 			gen.AddCrls(sp.GetCrls("Collection"));
 
-			bOut.SetLength(0);
-
-			sigOut = gen.Open(bOut, true);
+            bOut.SetLength(0);
 
+            sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedData sd = new CmsSignedData(
+            CmsSignedData sd = new CmsSignedData(
 				new CmsProcessableByteArray(testBytes), bOut.ToArray());
 
-			Assert.AreEqual(1, sd.GetSignerInfos().GetSigners().Count);
+            Assert.AreEqual(1, sd.GetSignerInfos().GetSigners().Count);
 
-			VerifyEncodedData(bOut);
+            VerifyEncodedData(bOut);
 		}
 
 		private static readonly DerObjectIdentifier dummyOid1 = new DerObjectIdentifier("1.2.3");
@@ -718,32 +609,23 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 	    public void TestSha1WithRsaEncapsulatedSubjectKeyID()
 	    {
-	        IList certList = new ArrayList();
 	        MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 	        gen.AddSigner(OrigKP.Private,
 				CmsTestUtil.CreateSubjectKeyId(OrigCert.GetPublicKey()).GetKeyIdentifier(),
 				CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut, true);
-
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
-			sigOut.Write(testBytes, 0, testBytes.Length);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
+            Stream sigOut = gen.Open(bOut, true);
+            sigOut.Write(testBytes, 0, testBytes.Length);
 			sigOut.Close();
 
-			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
+            CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
 			sp.GetSignedContent().Drain();
 
@@ -762,55 +644,42 @@ namespace Org.BouncyCastle.Cms.Tests
 			// try using existing signer
 			//
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigners(sp.GetSignerInfos());
-
-//			gen.AddCertificatesAndCRLs(sp.getCertificatesAndCRLs("Collection", "BC"));
+//			gen.AddCertificatesAndCRLs(sp.GetCertificatesAndCrls("Collection", "BC"));
 			gen.AddCertificates(sp.GetCertificates("Collection"));
 
-			bOut.SetLength(0);
-
-			sigOut = gen.Open(bOut, true);
+            bOut.SetLength(0);
 
+            sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedData sd = new CmsSignedData(new CmsProcessableByteArray(testBytes), bOut.ToArray());
+            CmsSignedData sd = new CmsSignedData(new CmsProcessableByteArray(testBytes), bOut.ToArray());
 
 			Assert.AreEqual(1, sd.GetSignerInfos().GetSigners().Count);
 
 	        VerifyEncodedData(bOut);
 	    }
 
-		[Test]
+        [Test]
 		public void TestAttributeGenerators()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
 			CmsAttributeTableGenerator signedGen = new SignedGenAttributeTableGenerator();
 			CmsAttributeTableGenerator unsignedGen = new UnsignedGenAttributeTableGenerator();
 
-			gen.AddSigner(OrigKP.Private, OrigCert,
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            gen.AddSigner(OrigKP.Private, OrigCert,
 				CmsSignedDataStreamGenerator.DigestSha1, signedGen, unsignedGen);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut, true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+			Stream sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
 			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
@@ -826,59 +695,44 @@ namespace Org.BouncyCastle.Cms.Tests
 
 			foreach (SignerInformation signer in signers.GetSigners())
 			{
-				checkAttribute(signer.GetContentDigest(), signer.SignedAttributes[dummyOid1]);
-				checkAttribute(signer.GetSignature(), signer.UnsignedAttributes[dummyOid2]);
+				CheckAttribute(signer.GetContentDigest(), signer.SignedAttributes[dummyOid1]);
+				CheckAttribute(signer.GetSignature(), signer.UnsignedAttributes[dummyOid2]);
 			}
 		}
 
-		private void checkAttribute(
-			byte[]				expected,
-			Asn1.Cms.Attribute	attr)
+		private void CheckAttribute(byte[] expected, Asn1.Cms.Attribute	attr)
 		{
 			DerOctetString value = (DerOctetString)attr.AttrValues[0];
 
-			Assert.AreEqual(new DerOctetString(expected), value);
+            Assert.AreEqual(new DerOctetString(expected), value);
 		}
 
-		[Test]
+        [Test]
 		public void TestWithAttributeCertificate()
 		{
-			IList certList = new ArrayList();
-
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			IX509AttributeCertificate attrCert = CmsTestUtil.GetAttributeCertificate();
+            IX509AttributeCertificate attrCert = CmsTestUtil.GetAttributeCertificate();
 
-			ArrayList attrCerts = new ArrayList();
-			attrCerts.Add(attrCert);
-			IX509Store store = X509StoreFactory.Create(
-				"AttributeCertificate/Collection",
-				new X509CollectionStoreParameters(attrCerts));
+            IX509Store store = CmsTestUtil.MakeAttrCertStore(attrCert);
 
-			gen.AddAttributeCertificates(store);
+            gen.AddAttributeCertificates(store);
 
-			MemoryStream bOut = new MemoryStream();
+            MemoryStream bOut = new MemoryStream();
 
-			Stream sigOut = gen.Open(bOut, true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+			Stream sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
+            CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
-			sp.GetSignedContent().Drain();
+            sp.GetSignedContent().Drain();
 
 			Assert.AreEqual(4, sp.Version);
 
@@ -894,27 +748,17 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSignerStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 			byte[] data = Encoding.ASCII.GetBytes(TestMessage);
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
 			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
 			Stream sigOut = gen.Open(bOut, false);
-
 			sigOut.Write(data, 0, data.Length);
-
 			sigOut.Close();
 
 			CheckSigParseable(bOut.ToArray());
@@ -927,18 +771,15 @@ namespace Org.BouncyCastle.Cms.Tests
 			bOut.SetLength(0);
 
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha224);
 
-			gen.AddCertificates(x509Certs);
+            gen.AddCertificates(x509Certs);
 
 			sigOut = gen.Open(bOut);
-
 			sigOut.Write(data, 0, data.Length);
-
 			sigOut.Close();
 
-			CheckSigParseable(bOut.ToArray());
+            CheckSigParseable(bOut.ToArray());
 
 			CmsSignedData sd = new CmsSignedData(bOut.ToArray());
 
@@ -953,7 +794,7 @@ namespace Org.BouncyCastle.Cms.Tests
 
 			IEnumerator signerEnum = sd.GetSignerInfos().GetSigners().GetEnumerator();
 			signerEnum.MoveNext();
-			SignerInformation signer = (SignerInformation) signerEnum.Current;
+			SignerInformation signer = (SignerInformation)signerEnum.Current;
 
 			Assert.AreEqual(signer.DigestAlgOid, CmsSignedDataStreamGenerator.DigestSha224);
 
@@ -968,27 +809,18 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestEncapsulatedSignerStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut, true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+			Stream sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
 			//
@@ -999,20 +831,16 @@ namespace Org.BouncyCastle.Cms.Tests
 			bOut.SetLength(0);
 
 			gen = new CmsSignedDataStreamGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha224);
-
 			gen.AddCertificates(x509Certs);
 
-			sigOut = gen.Open(bOut, true);
-
+            sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedData sd = new CmsSignedData(bOut.ToArray());
+            CmsSignedData sd = new CmsSignedData(bOut.ToArray());
 
-			//
+            //
 			// replace signer
 			//
 			MemoryStream newOut = new MemoryStream();
@@ -1037,40 +865,25 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestCertStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 			byte[] data = Encoding.ASCII.GetBytes(TestMessage);
 
-			certList.Add(OrigDsaCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigDsaCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut);
-
+            Stream sigOut = gen.Open(bOut);
 			sigOut.Write(data, 0, data.Length);
-
 			sigOut.Close();
 
-			CheckSigParseable(bOut.ToArray());
+            CheckSigParseable(bOut.ToArray());
 
-			//
+            //
 			// create new certstore with the right certificates
 			//
-			certList = new ArrayList();
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
 			//
 			// replace certs
@@ -1090,14 +903,9 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestEncapsulatedCertStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigDsaCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigDsaCert);
 
 			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 
@@ -1115,15 +923,9 @@ namespace Org.BouncyCastle.Cms.Tests
 			//
 			// create new certstore with the right certificates
 			//
-			certList = new ArrayList();
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
+            x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
-			x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			//
+            //
 			// replace certs
 			//
 			MemoryStream original = new MemoryStream(bOut.ToArray(), false);
@@ -1141,30 +943,21 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestCertOrdering1()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut, true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+			Stream sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
+            CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
 			sp.GetSignedContent().Drain();
 			x509Certs = sp.GetCertificates("Collection");
@@ -1178,32 +971,23 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestCertOrdering2()
 		{
-			IList certList = new ArrayList();
 			MemoryStream bOut = new MemoryStream();
 
-			certList.Add(SignCert);
-			certList.Add(OrigCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignCert, OrigCert);
 
+            CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataStreamGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			Stream sigOut = gen.Open(bOut, true);
+            byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
 
-			byte[] testBytes = Encoding.ASCII.GetBytes(TestMessage);
+            Stream sigOut = gen.Open(bOut, true);
 			sigOut.Write(testBytes, 0, testBytes.Length);
-
 			sigOut.Close();
 
-			CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
+            CmsSignedDataParser sp = new CmsSignedDataParser(bOut.ToArray());
 
-			sp.GetSignedContent().Drain();
+            sp.GetSignedContent().Drain();
 			x509Certs = sp.GetCertificates("Collection");
 			ArrayList a = new ArrayList(x509Certs.GetMatches(null));
 
@@ -1215,13 +999,7 @@ namespace Org.BouncyCastle.Cms.Tests
         [Test]
         public void TestCertsOnly()
         {
-            IList certList = new ArrayList();
-            certList.Add(OrigCert);
-            certList.Add(SignCert);
-
-            IX509Store x509Certs = X509StoreFactory.Create(
-                "Certificate/Collection",
-                new X509CollectionStoreParameters(certList));
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
             MemoryStream bOut = new MemoryStream();
 
diff --git a/crypto/test/src/cms/test/SignedDataTest.cs b/crypto/test/src/cms/test/SignedDataTest.cs
index 96f00eadc..2b5d147f6 100644
--- a/crypto/test/src/cms/test/SignedDataTest.cs
+++ b/crypto/test/src/cms/test/SignedDataTest.cs
@@ -17,6 +17,7 @@ using Org.BouncyCastle.Utilities.IO;
 using Org.BouncyCastle.Utilities.Test;
 using Org.BouncyCastle.X509;
 using Org.BouncyCastle.X509.Store;
+using Org.BouncyCastle.Crypto.Operators;
 
 namespace Org.BouncyCastle.Cms.Tests
 {
@@ -413,64 +414,37 @@ namespace Org.BouncyCastle.Cms.Tests
 			byte[] data = Encoding.ASCII.GetBytes("Hello World!");
 			CmsProcessable msg = new CmsProcessableByteArray(data);
 
-			IList certList = new ArrayList();
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestMD5);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData s = gen.Generate(msg);
+            CmsSignedData s = gen.Generate(msg);
 
 			IDictionary hashes = new Hashtable();
-			hashes.Add(CmsSignedDataGenerator.DigestSha1, CalculateHash("SHA1", data));
-			hashes.Add(CmsSignedDataGenerator.DigestMD5, CalculateHash("MD5", data));
+			hashes.Add(CmsSignedDataGenerator.DigestSha1, DigestUtilities.CalculateDigest("SHA1", data));
+            hashes.Add(CmsSignedDataGenerator.DigestMD5, DigestUtilities.CalculateDigest("MD5", data));
 
 			s = new CmsSignedData(hashes, s.GetEncoded());
 
 			VerifySignatures(s, null);
 		}
 
-		private byte[] CalculateHash(
-			string	digestName,
-			byte[]	data)
-		{
-			IDigest digest = DigestUtilities.GetDigest(digestName);
-			digest.BlockUpdate(data, 0, data.Length);
-			return DigestUtilities.DoFinal(digest);
-		}
-
-		[Test]
+        [Test]
 		public void TestSha1AndMD5WithRsaEncapsulatedRepeated()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
 			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestMD5);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData s = gen.Generate(msg, true);
+            CmsSignedData s = gen.Generate(msg, true);
 
 			s = new CmsSignedData(ContentInfo.GetInstance(Asn1Object.FromByteArray(s.GetEncoded())));
 
@@ -549,74 +523,145 @@ namespace Org.BouncyCastle.Cms.Tests
 			CheckSignerStoreReplacement(s, signers);
 		}
 
-		// NB: C# build doesn't support "no attributes" version of CmsSignedDataGenerator.Generate
-//		[Test]
-//		public void TestSha1WithRsaNoAttributes()
-//		{
-//			IList certList = new ArrayList();
-//			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello world!"));
-//
-//			certList.Add(OrigCert);
-//			certList.Add(SignCert);
-//
-//			IX509Store x509Certs = X509StoreFactory.Create(
-//				"Certificate/Collection",
-//				new X509CollectionStoreParameters(certList));
-//
-//			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
-//
-//			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-//
-//			gen.AddCertificates(x509Certs);
-//
-//			CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false, false);
-//
-//			//
-//			// compute expected content digest
-//			//
-//			IDigest md = DigestUtilities.GetDigest("SHA1");
-//
-//			byte[] testBytes = Encoding.ASCII.GetBytes("Hello world!");
-//			md.BlockUpdate(testBytes, 0, testBytes.Length);
-//			byte[] hash = DigestUtilities.DoFinal(md);
-//
-//			VerifySignatures(s, hash);
-//		}
+        [Test]
+        public void TestSha1AndMD5WithRsaEncapsulatedRepeatedWithSignerInfoGen()
+        {
+            CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-		[Test]
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
+
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            gen.AddSignerInfoGenerator(new SignerInfoGeneratorBuilder().Build(
+                new Asn1SignatureCalculator("SHA1withRSA", OrigKP.Private), OrigCert));
+            gen.AddSignerInfoGenerator(new SignerInfoGeneratorBuilder().Build(
+                new Asn1SignatureCalculator("MD5withRSA", OrigKP.Private), OrigCert));
+
+            gen.AddCertificates(x509Certs);
+
+            CmsSignedData s = gen.Generate(msg, true);
+
+            s = new CmsSignedData(ContentInfo.GetInstance(Asn1Object.FromByteArray(s.GetEncoded())));
+
+            x509Certs = s.GetCertificates("Collection");
+
+            SignerInformationStore signers = s.GetSignerInfos();
+
+            Assert.AreEqual(2, signers.Count);
+
+            SignerID sid = null;
+            ICollection c = signers.GetSigners();
+
+            foreach (SignerInformation signer in c)
+            {
+                ICollection certCollection = x509Certs.GetMatches(signer.SignerID);
+
+                IEnumerator certEnum = certCollection.GetEnumerator();
+
+                certEnum.MoveNext();
+                X509Certificate cert = (X509Certificate)certEnum.Current;
+
+                sid = signer.SignerID;
+
+                Assert.IsTrue(signer.Verify(cert));
+
+                //
+                // check content digest
+                //
+
+                byte[] contentDigest = (byte[])gen.GetGeneratedDigests()[signer.DigestAlgOid];
+
+                AttributeTable table = signer.SignedAttributes;
+                Asn1.Cms.Attribute hash = table[CmsAttributes.MessageDigest];
+
+                Assert.IsTrue(Arrays.AreEqual(contentDigest, ((Asn1OctetString)hash.AttrValues[0]).GetOctets()));
+            }
+
+            c = signers.GetSigners(sid);
+
+            Assert.AreEqual(2, c.Count);
+
+            //
+            // try using existing signer
+            //
+
+            gen = new CmsSignedDataGenerator();
+
+            gen.AddSigners(s.GetSignerInfos());
+
+            gen.AddCertificates(s.GetCertificates("Collection"));
+            gen.AddCrls(s.GetCrls("Collection"));
+
+            s = gen.Generate(msg, true);
+
+            s = new CmsSignedData(ContentInfo.GetInstance(Asn1Object.FromByteArray(s.GetEncoded())));
+
+            x509Certs = s.GetCertificates("Collection");
+
+            signers = s.GetSignerInfos();
+            c = signers.GetSigners();
+
+            Assert.AreEqual(2, c.Count);
+
+            foreach (SignerInformation signer in c)
+            {
+                ICollection certCollection = x509Certs.GetMatches(signer.SignerID);
+
+                IEnumerator certEnum = certCollection.GetEnumerator();
+
+                certEnum.MoveNext();
+                X509Certificate cert = (X509Certificate)certEnum.Current;
+
+                Assert.AreEqual(true, signer.Verify(cert));
+            }
+
+            CheckSignerStoreReplacement(s, signers);
+        }
+
+        // NB: C# build doesn't support "no attributes" version of CmsSignedDataGenerator.Generate
+        //[Test]
+        //public void TestSha1WithRsaNoAttributes()
+        //{
+        //    CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello world!"));
+
+        //    IX509Store x509Certs = MakeCertStore(OrigCert, SignCert);
+
+        //    CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+        //    gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
+        //    gen.AddCertificates(x509Certs);
+
+        //    CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false, false);
+
+        //    byte[] testBytes = Encoding.ASCII.GetBytes("Hello world!");
+
+        //    // compute expected content digest
+        //    byte[] hash = DigestUtilities.CalculateDigest("SHA1", testBytes);
+
+        //    VerifySignatures(s, hash);
+        //}
+
+        [Test]
 		public void TestSha1WithRsaAndAttributeTable()
 		{
 			byte[] testBytes = Encoding.ASCII.GetBytes("Hello world!");
-
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(testBytes);
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
-
-			IDigest md = DigestUtilities.GetDigest("SHA1");
-			md.BlockUpdate(testBytes, 0, testBytes.Length);
-			byte[] hash = DigestUtilities.DoFinal(md);
+            byte[] hash = DigestUtilities.CalculateDigest("SHA1", testBytes);
 
 			Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.MessageDigest,
 				new DerSet(new DerOctetString(hash)));
 
 			Asn1EncodableVector v = new Asn1EncodableVector(attr);
 
-			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1,
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            gen.AddSigner(SignKP.Private, SignCert, CmsSignedDataGenerator.DigestSha1,
 				new AttributeTable(v), null);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, null, false);
+            CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, null, false);
 
-			//
+            //
 			// the signature is detached, so need to add msg before passing on
 			//
 			s = new CmsSignedData(msg, s.GetEncoded());
@@ -693,7 +738,31 @@ namespace Org.BouncyCastle.Cms.Tests
 			EncapsulatedTest(SignKP, SignCert, CmsSignedDataGenerator.DigestRipeMD256);
 		}
 
-		[Test]
+        [Test]
+        public void TestSha224WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha224);
+        }
+
+        [Test]
+        public void TestSha256WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha256);
+        }
+
+        [Test]
+        public void TestSha384WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha384);
+        }
+
+        [Test]
+        public void TestSha512WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha512);
+        }
+
+        [Test]
 		public void TestECDsaEncapsulated()
 		{
 			EncapsulatedTest(SignECDsaKP, SignECDsaCert, CmsSignedDataGenerator.DigestSha1);
@@ -772,26 +841,13 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSha1WithRsaCounterSignature()
 		{
-			IList certList = new ArrayList();
-			IList crlList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(SignCert);
-			certList.Add(OrigCert);
-
-			crlList.Add(SignCrl);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-			IX509Store x509Crls = X509StoreFactory.Create(
-				"CRL/Collection",
-				new X509CollectionStoreParameters(crlList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignCert, OrigCert);
+            IX509Store x509Crls = CmsTestUtil.MakeCrlStore(SignCrl);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(SignKP.Private, SignCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 			gen.AddCrls(x509Crls);
 
@@ -825,33 +881,21 @@ namespace Org.BouncyCastle.Cms.Tests
 			string	digestName,
 			string	digestOID)
 		{
-			IList certList = new ArrayList();
 			byte[] msgBytes = Encoding.ASCII.GetBytes("Hello World!");
 			CmsProcessable msg = new CmsProcessableByteArray(msgBytes);
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.EncryptionRsaPss, digestOID);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false);
+            CmsSignedData s = gen.Generate(CmsSignedDataGenerator.Data, msg, false);
 
-			//
-			// compute expected content digest
-			//
-			IDigest md = DigestUtilities.GetDigest(digestName);
-			md.BlockUpdate(msgBytes, 0, msgBytes.Length);
-			byte[] expectedDigest = DigestUtilities.DoFinal(md);
+            // compute expected content digest
+            byte[] expectedDigest = DigestUtilities.CalculateDigest(digestName, msgBytes);
 
-			VerifySignatures(s, expectedDigest);
+            VerifySignatures(s, expectedDigest);
 		}
 
 		private void SubjectKeyIDTest(
@@ -859,32 +903,19 @@ namespace Org.BouncyCastle.Cms.Tests
 			X509Certificate			signatureCert,
 			string					digestAlgorithm)
 		{
-			IList certList = new ArrayList();
-			IList crlList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(signatureCert);
-			certList.Add(OrigCert);
-
-			crlList.Add(SignCrl);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-			IX509Store x509Crls = X509StoreFactory.Create(
-				"CRL/Collection",
-				new X509CollectionStoreParameters(crlList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(signatureCert, OrigCert);
+            IX509Store x509Crls = CmsTestUtil.MakeCrlStore(SignCrl);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(signaturePair.Private,
 				CmsTestUtil.CreateSubjectKeyId(signatureCert.GetPublicKey()).GetKeyIdentifier(),
 				digestAlgorithm);
-
 			gen.AddCertificates(x509Certs);
 			gen.AddCrls(x509Crls);
 
-			CmsSignedData s = gen.Generate(msg, true);
+            CmsSignedData s = gen.Generate(msg, true);
 
 			Assert.AreEqual(3, s.Version);
 
@@ -962,26 +993,13 @@ namespace Org.BouncyCastle.Cms.Tests
 			X509Certificate			signatureCert,
 			string					digestAlgorithm)
 		{
-			IList certList = new ArrayList();
-			IList crlList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(signatureCert);
-			certList.Add(OrigCert);
-
-			crlList.Add(SignCrl);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-			IX509Store x509Crls = X509StoreFactory.Create(
-				"CRL/Collection",
-				new X509CollectionStoreParameters(crlList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(signatureCert, OrigCert);
+            IX509Store x509Crls = CmsTestUtil.MakeCrlStore(SignCrl);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(signaturePair.Private, signatureCert, digestAlgorithm);
-
 			gen.AddCertificates(x509Certs);
 			gen.AddCrls(x509Crls);
 
@@ -1004,6 +1022,8 @@ namespace Org.BouncyCastle.Cms.Tests
 				certEnum.MoveNext();
 				X509Certificate cert = (X509Certificate) certEnum.Current;
 
+                Assert.AreEqual(digestAlgorithm, signer.DigestAlgOid);
+
 				Assert.IsTrue(signer.Verify(cert));
 			}
 
@@ -1105,22 +1125,13 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestNullContentWithSigner()
 		{
-			IList certList = new ArrayList();
-
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData s = gen.Generate(null, false);
+            CmsSignedData s = gen.Generate(null, false);
 
 			s = new CmsSignedData(ContentInfo.GetInstance(Asn1Object.FromByteArray(s.GetEncoded())));
 
@@ -1130,29 +1141,17 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestWithAttributeCertificate()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(SignDsaCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignDsaCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			IX509AttributeCertificate attrCert = CmsTestUtil.GetAttributeCertificate();
-
-			ArrayList attrCerts = new ArrayList();
-			attrCerts.Add(attrCert);
+            IX509AttributeCertificate attrCert = CmsTestUtil.GetAttributeCertificate();
 
-			IX509Store store = X509StoreFactory.Create(
-				"AttributeCertificate/Collection",
-				new X509CollectionStoreParameters(attrCerts));
+            IX509Store store = CmsTestUtil.MakeAttrCertStore(attrCert);
 
 			gen.AddAttributeCertificates(store);
 
@@ -1171,13 +1170,7 @@ namespace Org.BouncyCastle.Cms.Tests
 			//
 			// create new certstore
 			//
-			certList = new ArrayList();
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
 			//
 			// replace certs
@@ -1190,35 +1183,22 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestCertStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(SignDsaCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignDsaCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData sd = gen.Generate(msg);
+            CmsSignedData sd = gen.Generate(msg);
 
 			//
 			// create new certstore
 			//
-			certList = new ArrayList();
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
+            x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
-			//
+            //
 			// replace certs
 			//
 			sd = CmsSignedData.ReplaceCertificatesAndCrls(sd, x509Certs, null, null);
@@ -1229,35 +1209,22 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestEncapsulatedCertStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(SignDsaCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignDsaCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData sd = gen.Generate(msg, true);
+            CmsSignedData sd = gen.Generate(msg, true);
 
 			//
 			// create new certstore
 			//
-			certList = new ArrayList();
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
+            x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
-			x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			//
+            //
 			// replace certs
 			//
 			sd = CmsSignedData.ReplaceCertificatesAndCrls(sd, x509Certs, null, null);
@@ -1268,24 +1235,15 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestCertOrdering1()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-			certList.Add(SignDsaCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert, SignDsaCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData sd = gen.Generate(msg, true);
+            CmsSignedData sd = gen.Generate(msg, true);
 
 			x509Certs = sd.GetCertificates("Collection");
 			ArrayList a = new ArrayList(x509Certs.GetMatches(null));
@@ -1299,29 +1257,20 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestCertOrdering2()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(SignCert);
-			certList.Add(SignDsaCert);
-			certList.Add(OrigCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-	
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(SignCert, SignDsaCert, OrigCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
 			CmsSignedData sd = gen.Generate(msg, true);
 
-			x509Certs = sd.GetCertificates("Collection");
-			ArrayList a = new ArrayList(x509Certs.GetMatches(null));
+            x509Certs = sd.GetCertificates("Collection");
+            ArrayList a = new ArrayList(x509Certs.GetMatches(null));
 
-			Assert.AreEqual(3, a.Count);
+            Assert.AreEqual(3, a.Count);
 			Assert.AreEqual(SignCert, a[0]);
 			Assert.AreEqual(SignDsaCert, a[1]);
 			Assert.AreEqual(OrigCert, a[2]);
@@ -1330,36 +1279,26 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestSignerStoreReplacement()
 		{
-			IList certList = new ArrayList();
 			CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
 
-			certList.Add(OrigCert);
-			certList.Add(SignCert);
-
-			IX509Store x509Certs = X509StoreFactory.Create(
-				"Certificate/Collection",
-				new X509CollectionStoreParameters(certList));
-
-			CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
 
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha1);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData original = gen.Generate(msg, true);
+            CmsSignedData original = gen.Generate(msg, true);
 
-			//
+            //
 			// create new Signer
 			//
 			gen = new CmsSignedDataGenerator();
-
 			gen.AddSigner(OrigKP.Private, OrigCert, CmsSignedDataGenerator.DigestSha224);
-
 			gen.AddCertificates(x509Certs);
 
-			CmsSignedData newSD = gen.Generate(msg, true);
+            CmsSignedData newSD = gen.Generate(msg, true);
 
-			//
+            //
 			// replace signer
 			//
 			CmsSignedData sd = CmsSignedData.ReplaceSigners(original, newSD.GetSignerInfos());
@@ -1382,17 +1321,17 @@ namespace Org.BouncyCastle.Cms.Tests
 		[Test]
 		public void TestEncapsulatedSamples()
 		{
-			doTestSample("PSSSignDataSHA1Enc.sig");
-			doTestSample("PSSSignDataSHA256Enc.sig");
-			doTestSample("PSSSignDataSHA512Enc.sig");
+			DoTestSample("PSSSignDataSHA1Enc.sig");
+			DoTestSample("PSSSignDataSHA256Enc.sig");
+			DoTestSample("PSSSignDataSHA512Enc.sig");
 		}
 
 		[Test]
 		public void TestSamples()
 		{
-			doTestSample("PSSSignData.data", "PSSSignDataSHA1.sig");
-			doTestSample("PSSSignData.data", "PSSSignDataSHA256.sig");
-			doTestSample("PSSSignData.data", "PSSSignDataSHA512.sig");
+			DoTestSample("PSSSignData.data", "PSSSignDataSHA1.sig");
+			DoTestSample("PSSSignData.data", "PSSSignDataSHA256.sig");
+			DoTestSample("PSSSignData.data", "PSSSignDataSHA512.sig");
 		}
 
 		[Test]
@@ -1421,31 +1360,27 @@ namespace Org.BouncyCastle.Cms.Tests
 			VerifySignatures(sig);
 		}
 
-		private void doTestSample(
-			string sigName)
+		private void DoTestSample(string sigName)
 		{
 			CmsSignedData sig = new CmsSignedData(GetInput(sigName));
 			VerifySignatures(sig);
 		}
 
-		private void doTestSample(
-			string	messageName,
-			string	sigName)
+        private void DoTestSample(string messageName, string sigName)
 		{
 			CmsSignedData sig = new CmsSignedData(
 				new CmsProcessableByteArray(GetInput(messageName)),
 				GetInput(sigName));
 
-			VerifySignatures(sig);
+            VerifySignatures(sig);
 		}
 
-		private byte[] GetInput(
-			string name)
+        private byte[] GetInput(string name)
 		{
 			return Streams.ReadAll(SimpleTest.GetTestDataAsStream("cms.sigs." + name));
 		}
 
-		[Test]
+        [Test]
 		public void TestForMultipleCounterSignatures()
 		{
 			CmsSignedData sd = new CmsSignedData(xtraCounterSig);
@@ -1476,5 +1411,5 @@ namespace Org.BouncyCastle.Cms.Tests
 				Assert.IsTrue(signer.Verify(cert));
 			}
 		}
-	}
+    }
 }
diff --git a/crypto/test/src/crypto/io/test/AllTests.cs b/crypto/test/src/crypto/io/test/AllTests.cs
index 0296a2dc0..5c8c759f9 100644
--- a/crypto/test/src/crypto/io/test/AllTests.cs
+++ b/crypto/test/src/crypto/io/test/AllTests.cs
@@ -7,21 +7,20 @@ namespace Org.BouncyCastle.Crypto.IO.Tests
 {
 	public class AllTests
 	{
-		public static void Main(
-			string[] args)
-		{
-//            junit.textui.TestRunner.run(suite());
-			EventListener el = new NullListener();
-			suite().Run(el);
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		public static TestSuite suite()
-		{
-			TestSuite suite = new TestSuite("IO tests");
-
-			suite.Add(new CipherStreamTest());
-
-			return suite;
-		}
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("IO tests");
+                suite.Add(new CipherStreamTest());
+                return suite;
+            }
+        }
 	}
 }
diff --git a/crypto/test/src/crypto/test/AeadTestUtilities.cs b/crypto/test/src/crypto/test/AeadTestUtilities.cs
new file mode 100644
index 000000000..40f334202
--- /dev/null
+++ b/crypto/test/src/crypto/test/AeadTestUtilities.cs
@@ -0,0 +1,14 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    public class AeadTestUtilities
+    {
+        internal static AeadParameters ReuseKey(AeadParameters p)
+        {
+            return new AeadParameters(null, p.MacSize, p.GetNonce(), p.GetAssociatedText());
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/AllTests.cs b/crypto/test/src/crypto/test/AllTests.cs
index 1cb1b965d..3d8ef5602 100644
--- a/crypto/test/src/crypto/test/AllTests.cs
+++ b/crypto/test/src/crypto/test/AllTests.cs
@@ -10,19 +10,24 @@ namespace Org.BouncyCastle.Crypto.Tests
 	[TestFixture]
 	public class AllTests
 	{
-		[Suite]
-		public static TestSuite Suite
-		{
-			get
-			{
-				TestSuite suite = new TestSuite("Lightweight Crypto Tests");
-				suite.Add(new AllTests());
-		        suite.Add(new GcmReorderTest());
-				return suite;
-			}
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		[Test]
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("Lightweight Crypto Tests");
+                suite.Add(new AllTests());
+                suite.Add(new GcmReorderTest());
+                return suite;
+            }
+        }
+
+        [Test]
 		public void TestCrypto()
 		{
 			foreach (Org.BouncyCastle.Utilities.Test.ITest test in RegressionTest.tests)
@@ -35,13 +40,5 @@ namespace Org.BouncyCastle.Crypto.Tests
 				}
 			}
 		}
-
-        public static void Main(
-			string[] args)
-        {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            Suite.Run(el);
-        }
 	}
 }
diff --git a/crypto/test/src/crypto/test/CCMTest.cs b/crypto/test/src/crypto/test/CCMTest.cs
index 4a54fb4f9..8c46e11e7 100644
--- a/crypto/test/src/crypto/test/CCMTest.cs
+++ b/crypto/test/src/crypto/test/CCMTest.cs
@@ -81,7 +81,38 @@ namespace Org.BouncyCastle.Crypto.Tests
 			//
 			checkVectors(4, ccm, K4, 112, N4, A4, A4, T5, C5);
 
-			//
+            // decryption with output specified, non-zero offset.
+            ccm.Init(false, new AeadParameters(new KeyParameter(K2), 48, N2, A2));
+
+            byte[] inBuf = new byte[C2.Length + 10];
+            byte[] outBuf = new byte[ccm.GetOutputSize(C2.Length) + 10];
+
+            Array.Copy(C2, 0, inBuf, 10, C2.Length);
+
+            int len = ccm.ProcessPacket(inBuf, 10, C2.Length, outBuf, 10);
+            byte[] output = ccm.ProcessPacket(C2, 0, C2.Length);
+
+            if (len != output.Length || !isEqual(output, outBuf, 10))
+            {
+                Fail("decryption output incorrect");
+            }
+
+            // encryption with output specified, non-zero offset.
+            ccm.Init(true, new AeadParameters(new KeyParameter(K2), 48, N2, A2));
+
+            int inLen = len;
+            inBuf = outBuf;
+            outBuf = new byte[ccm.GetOutputSize(inLen) + 10];
+
+            len = ccm.ProcessPacket(inBuf, 10, inLen, outBuf, 10);
+            output = ccm.ProcessPacket(inBuf, 10, inLen);
+
+            if (len != output.Length || !isEqual(output, outBuf, 10))
+            {
+                Fail("encryption output incorrect");
+            }
+
+            //
 			// exception tests
 			//
 
@@ -121,6 +152,17 @@ namespace Org.BouncyCastle.Crypto.Tests
 			}
 		}
 
+        private bool isEqual(byte[] exp, byte[] other, int off)
+        {
+            for (int i = 0; i != exp.Length; i++)
+            {
+                if (exp[i] != other[off + i])
+                    return false;
+            }
+
+            return true;
+        }
+
 		private void checkVectors(
 			int count,
 			CcmBlockCipher ccm,
@@ -203,7 +245,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 			if (!AreEqual(p, dec))
 			{
-                Fail("decrypted stream fails to match in test " + count + " with " + additionalDataType);
+                Fail("decrypted stream fails to match in test " + count + " with " + additionalDataType,
+                    Hex.ToHexString(p), Hex.ToHexString(dec));
             }
 
 			if (!AreEqual(t, ccm.GetMac()))
diff --git a/crypto/test/src/crypto/test/DeterministicDSATest.cs b/crypto/test/src/crypto/test/DeterministicDSATest.cs
new file mode 100644
index 000000000..914a770bd
--- /dev/null
+++ b/crypto/test/src/crypto/test/DeterministicDSATest.cs
@@ -0,0 +1,511 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    /// <summary>
+    /// Tests are taken from RFC 6979 - "Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)".
+    /// </summary>
+    [TestFixture]
+    public class DeterministicDsaTest
+        :   SimpleTest
+    {
+        public static readonly byte[] SAMPLE = Hex.Decode("73616d706c65"); // "sample"
+        public static readonly byte[] TEST = Hex.Decode("74657374"); // "test"
+
+        // test vectors from appendix in RFC 6979
+        private void TestHMacDeterministic()
+        {
+            DsaParameters dsaParameters = new DsaParameters(
+                new BigInteger("86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447" +
+                               "E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88" +
+                               "73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C" +
+                               "881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779", 16),
+                new BigInteger("996F967F6C8E388D9E28D01E205FBA957A5698B1", 16),
+                new BigInteger("07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D" +
+                               "89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD" +
+                               "87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4" +
+                               "17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD", 16));
+
+            DsaPrivateKeyParameters privKey = new DsaPrivateKeyParameters(new BigInteger("411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", 16), dsaParameters);
+
+            DoTestHMacDetDsaSample(new Sha1Digest(), privKey, new BigInteger("2E1A0C2562B2912CAAF89186FB0F42001585DA55", 16), new BigInteger("29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5", 16));
+            DoTestHMacDetDsaSample(new Sha224Digest(), privKey, new BigInteger("4BC3B686AEA70145856814A6F1BB53346F02101E", 16), new BigInteger("410697B92295D994D21EDD2F4ADA85566F6F94C1", 16));
+            DoTestHMacDetDsaSample(new Sha256Digest(), privKey, new BigInteger("81F2F5850BE5BC123C43F71A3033E9384611C545", 16), new BigInteger("4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89", 16));
+            DoTestHMacDetDsaSample(new Sha384Digest(), privKey, new BigInteger("07F2108557EE0E3921BC1774F1CA9B410B4CE65A", 16), new BigInteger("54DF70456C86FAC10FAB47C1949AB83F2C6F7595", 16));
+            DoTestHMacDetDsaSample(new Sha512Digest(), privKey, new BigInteger("16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B", 16), new BigInteger("02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C", 16));
+
+            DoTestHMacDetDsaTest(new Sha1Digest(), privKey, new BigInteger("42AB2052FD43E123F0607F115052A67DCD9C5C77", 16), new BigInteger("183916B0230D45B9931491D4C6B0BD2FB4AAF088", 16));
+            DoTestHMacDetDsaTest(new Sha224Digest(), privKey, new BigInteger("6868E9964E36C1689F6037F91F28D5F2C30610F2", 16), new BigInteger("49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F", 16));
+            DoTestHMacDetDsaTest(new Sha256Digest(), privKey, new BigInteger("22518C127299B0F6FDC9872B282B9E70D0790812", 16), new BigInteger("6837EC18F150D55DE95B5E29BE7AF5D01E4FE160", 16));
+            DoTestHMacDetDsaTest(new Sha384Digest(), privKey, new BigInteger("854CF929B58D73C3CBFDC421E8D5430CD6DB5E66", 16), new BigInteger("91D0E0F53E22F898D158380676A871A157CDA622", 16));
+            DoTestHMacDetDsaTest(new Sha512Digest(), privKey, new BigInteger("8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0", 16), new BigInteger("7C670C7AD72B6C050C109E1790008097125433E8", 16));
+
+            dsaParameters = new DsaParameters(
+                new BigInteger("9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
+                    "C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
+                    "FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
+                    "B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
+                    "35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
+                    "F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
+                    "92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
+                    "3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", 16),
+                new BigInteger("F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F", 16),
+                new BigInteger("5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
+                    "D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
+                    "6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
+                    "085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
+                    "AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
+                    "3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
+                    "BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
+                    "DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", 16));
+
+            privKey = new DsaPrivateKeyParameters(new BigInteger("69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC", 16), dsaParameters);
+
+            DoTestHMacDetDsaSample(new Sha1Digest(), privKey, new BigInteger("3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A", 16), new BigInteger("D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF", 16));
+            DoTestHMacDetDsaSample(new Sha224Digest(), privKey, new BigInteger("DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C", 16), new BigInteger("A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC", 16));
+            DoTestHMacDetDsaSample(new Sha256Digest(), privKey, new BigInteger("EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809", 16), new BigInteger("7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53", 16));
+            DoTestHMacDetDsaSample(new Sha384Digest(), privKey, new BigInteger("B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B", 16), new BigInteger("19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B", 16));
+            DoTestHMacDetDsaSample(new Sha512Digest(), privKey, new BigInteger("2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E", 16), new BigInteger("D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351", 16));
+
+            DoTestHMacDetDsaTest(new Sha1Digest(), privKey, new BigInteger("C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0", 16), new BigInteger("414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA", 16));
+            DoTestHMacDetDsaTest(new Sha224Digest(), privKey, new BigInteger("272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3", 16), new BigInteger("E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806", 16));
+            DoTestHMacDetDsaTest(new Sha256Digest(), privKey, new BigInteger("8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0", 16), new BigInteger("7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E", 16));
+            DoTestHMacDetDsaTest(new Sha384Digest(), privKey, new BigInteger("239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE", 16), new BigInteger("6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961", 16));
+            DoTestHMacDetDsaTest(new Sha512Digest(), privKey, new BigInteger("89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307", 16), new BigInteger("C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1", 16));
+        }
+
+        private void DoTestHMacDetDsaSample(IDigest digest, DsaPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+        {
+            DoTestHMacDetECDsa(new DsaSigner(new HMacDsaKCalculator(digest)), digest, SAMPLE, privKey, r, s);
+        }
+
+        private void DoTestHMacDetDsaTest(IDigest digest, DsaPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+        {
+            DoTestHMacDetECDsa(new DsaSigner(new HMacDsaKCalculator(digest)), digest, TEST, privKey, r, s);
+        }
+
+        // test vectors from appendix in RFC 6979
+        private void TestECHMacDeterministic()
+        {
+            X9ECParameters x9ECParameters = NistNamedCurves.GetByName("P-192");
+            ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("98C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF", 16), new BigInteger("57A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("A1F00DAD97AEEC91C95585F36200C65F3C01812AA60378F5", 16), new BigInteger("E07EC1304C7C6C9DEBBE980B9692668F81D4DE7922A0F97A", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55", 16), new BigInteger("CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("DA63BF0B9ABCF948FBB1E9167F136145F7A20426DCC287D5", 16), new BigInteger("C3AA2C960972BD7A2003A57E1C4C77F0578F8AE95E31EC5E", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("4D60C5AB1996BD848343B31C00850205E2EA6922DAC2E4B8", 16), new BigInteger("3F6E837448F027A1BF4B34E796E32A811CBB4050908D8F67", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("0F2141A0EBBC44D2E1AF90A50EBCFCE5E197B3B7D4DE036D", 16), new BigInteger("EB18BC9E1F3D7387500CB99CF5F7C157070A8961E38700B7", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("6945A1C1D1B2206B8145548F633BB61CEF04891BAF26ED34", 16), new BigInteger("B7FB7FDFC339C0B9BD61A9F5A8EAF9BE58FC5CBA2CB15293", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE", 16), new BigInteger("5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("B234B60B4DB75A733E19280A7A6034BD6B1EE88AF5332367", 16), new BigInteger("7994090B2D59BB782BE57E74A44C9A1C700413F8ABEFE77A", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("FE4F4AE86A58B6507946715934FE2D8FF9D95B6B098FE739", 16), new BigInteger("74CF5605C98FBA0E1EF34D4B5A1577A7DCF59457CAE52290", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("P-224");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("22226F9D40A96E19C4A301CE5B74B115303C0F3A4FD30FC257FB57AC", 16), new BigInteger("66D1CDD83E3AF75605DD6E2FEFF196D30AA7ED7A2EDF7AF475403D69", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E", 16), new BigInteger("A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("61AA3DA010E8E8406C656BC477A7A7189895E7E840CDFE8FF42307BA", 16), new BigInteger("BC814050DAB5D23770879494F9E0A680DC1AF7161991BDE692B10101", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("0B115E5E36F0F9EC81F1325A5952878D745E19D7BB3EABFABA77E953", 16), new BigInteger("830F34CCDFE826CCFDC81EB4129772E20E122348A2BBD889A1B1AF1D", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("074BD1D979D5F32BF958DDC61E4FB4872ADCAFEB2256497CDAC30397", 16), new BigInteger("A4CECA196C3D5A1FF31027B33185DC8EE43F288B21AB342E5D8EB084", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("DEAA646EC2AF2EA8AD53ED66B2E2DDAA49A12EFD8356561451F3E21C", 16), new BigInteger("95987796F6CF2062AB8135271DE56AE55366C045F6D9593F53787BD2", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019", 16), new BigInteger("902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("AD04DDE87B84747A243A631EA47A1BA6D1FAA059149AD2440DE6FBA6", 16), new BigInteger("178D49B1AE90E3D8B629BE3DB5683915F4E8C99FDF6E666CF37ADCFD", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("389B92682E399B26518A95506B52C03BC9379A9DADF3391A21FB0EA4", 16), new BigInteger("414A718ED3249FF6DBC5B50C27F71F01F070944DA22AB1F78F559AAB", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("049F050477C5ADD858CAC56208394B5A55BAEBBE887FDF765047C17C", 16), new BigInteger("077EB13E7005929CEFA3CD0403C7CDCC077ADF4E44F3C41B2F60ECFF", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("P-256");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32", 16), new BigInteger("6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F", 16), new BigInteger("B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716", 16), new BigInteger("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719", 16), new BigInteger("4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00", 16), new BigInteger("2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89", 16), new BigInteger("01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692", 16), new BigInteger("C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367", 16), new BigInteger("019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6", 16), new BigInteger("8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04", 16), new BigInteger("39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("P-384");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D8" +
+                                                                "96D5724E4C70A825F872C9EA60D2EDF5", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey, new BigInteger("EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA3" +
+                                                                        "7B9BA002899F6FDA3A4A9386790D4EB2", 16),
+                                                                new BigInteger("A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF" +
+                                                                        "26F49CA031D4857570CCB5CA4424A443", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366" +
+                                                                            "450F76EE3DE43F5A125333A6BE060122", 16),
+                                                                  new BigInteger("9DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E483" +
+                                                                            "4C082C03D83028EFBF93A3C23940CA8D", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33" +
+                                                                            "BDE1E888E63355D92FA2B3C36D8FB2CD", 16),
+                                                                    new BigInteger("F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEB" +
+                                                                            "EFDC63ECCD1AC42EC0CB8668A4FA0AB0", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C" +
+                                                                            "81A648152E44ACF96E36DD1E80FABE46", 16),
+                                                                    new BigInteger("99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94F" +
+                                                                            "A329C145786E679E7B82C71A38628AC8", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799C" +
+                                                                            "FE30F35CC900056D7C99CD7882433709", 16),
+                                                                    new BigInteger("512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112" +
+                                                                            "DC7CC3EF3446DEFCEB01A45C2667FDD5", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey, new BigInteger("4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678" +
+                                                                        "ACD9D29876DAF46638645F7F404B11C7", 16),
+                                                                new BigInteger("D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A29916" +
+                                                                        "95BA1C84541327E966FA7B50F7382282", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E624" +
+                                                                        "64A9A817C47FF78B8C11066B24080E72", 16),
+                                                                new BigInteger("07041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C614" +
+                                                                        "1C53EA5ABEF0D8231077A04540A96B66", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559" +
+                                                                        "F918EEDAF2293BE5B475CC8F0188636B", 16),
+                                                                new BigInteger("2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D" +
+                                                                        "51AB373F9845C0514EEFB14024787265", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB" +
+                                                                        "0542A7F0812998DA8F1DD3CA3CF023DB", 16),
+                                                                new BigInteger("DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E0" +
+                                                                        "6A739F040649A667BF3B828246BAA5A5", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D0" +
+                                                                        "6FB6495CD21B4B6E340FC236584FB277", 16),
+                                                                new BigInteger("976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B22463" +
+                                                                        "4A2092CD3792E0159AD9CEE37659C736", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("P-521");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75C" +
+                                                                "AA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83" +
+                                                                "538", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("0343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910" +
+                                                                                 "FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D" +
+                                                                                 "75D", 16),
+                                                                  new BigInteger("0E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D" +
+                                                                                 "5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5" +
+                                                                                 "D16", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("1776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A3" +
+                                                                                "0715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2E" +
+                                                                                "D2E", 16),
+                                                                  new BigInteger("050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17B" +
+                                                                                "A41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B" +
+                                                                                "41F", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("1511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659" +
+                                                                                 "D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E" +
+                                                                                 "1A7", 16),
+                                                                  new BigInteger("04A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916" +
+                                                                                  "E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7E" +
+                                                                                  "CFC", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("1EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4" +
+                                                                                "B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67" +
+                                                                                "451", 16),
+                                                                  new BigInteger("1F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5" +
+                                                                                  "FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65" +
+                                                                                  "D61", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F1" +
+                                                                                "74E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E37" +
+                                                                                "7FA", 16),
+                                                                  new BigInteger("0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF2" +
+                                                                                  "82623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A" +
+                                                                                  "67A", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("13BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0" +
+                                                                        "693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0" +
+                                                                        "367", 16),
+                                                                  new BigInteger("1E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90" +
+                                                                      "F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC91679" +
+                                                                      "7FF", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("1C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086" +
+                                                                        "BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE1" +
+                                                                        "7FB", 16),
+                                                                  new BigInteger("177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5" +
+                                                                      "BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD51" +
+                                                                      "9A4", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("00E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D807104" +
+                                                                        "2EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656" +
+                                                                        "AA8", 16),
+                                                                  new BigInteger("0CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9" +
+                                                                      "FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694" +
+                                                                      "E86", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("14BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C" +
+                                                                        "89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF60755" +
+                                                                        "78C", 16),
+                                                                  new BigInteger("133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0E" +
+                                                                      "D94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B" +
+                                                                      "979", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10" +
+                                                                        "CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47E" +
+                                                                        "E6D", 16),
+                                                                  new BigInteger("1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78" +
+                                                                      "A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4D" +
+                                                                      "CE3", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("B-163");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("35318FC447D48D7E6BC93B48617DDDEDF26AA658F", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("153FEBD179A69B6122DEBF5BC61EB947B24C93526", 16), new BigInteger("37AC9C670F8CF18045049BAE7DD35553545C19E49", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("0A379E69C44F9C16EA3215EA39EB1A9B5D58CC955", 16), new BigInteger("04BAFF5308DA2A7FE2C1742769265AD3ED1D24E74", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("134E00F78FC1CB9501675D91C401DE20DDF228CDC", 16), new BigInteger("373273AEC6C36CB7BAFBB1903A5F5EA6A1D50B624", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("29430B935AF8E77519B0CA4F6903B0B82E6A21A66", 16), new BigInteger("1EA1415306E9353FA5AA54BC7C2581DFBB888440D", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("0B2F177A99F9DF2D51CCAF55F015F326E4B65E7A0", 16), new BigInteger("0DF1FB4487E9B120C5E970EFE48F55E406306C3A1", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("256D4079C6C7169B8BC92529D701776A269D56308", 16), new BigInteger("341D3FFEC9F1EB6A6ACBE88E3C86A1C8FDEB8B8E1", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("28ECC6F1272CE80EA59DCF32F7AC2D861BA803393", 16), new BigInteger("0AD4AE2C06E60183C1567D2B82F19421FE3053CE2", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("227DF377B3FA50F90C1CB3CDCBBDBA552C1D35104", 16), new BigInteger("1F7BEAD92583FE920D353F368C1960D0E88B46A56", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("11811DAFEEA441845B6118A0DFEE8A0061231337D", 16), new BigInteger("36258301865EE48C5C6F91D63F62695002AB55B57", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("3B6BB95CA823BE2ED8E3972FF516EB8972D765571", 16), new BigInteger("13DC6F420628969DF900C3FCC48220B38BE24A541", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("B-233");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("07ADC13DD5BF34D1DDEEB50B2CE23B5F5E6D18067306D60C5F6FF11E5D3", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("015CC6FD78BB06E0878E71465515EA5A21A2C18E6FC77B4B158DBEB3944", 16), new BigInteger("0822A4A6C2EB2DF213A5E90BF40377956365EE8C4B4A5A4E2EB9270CB6A", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("05D9920B53471148E10502AB49AB7A3F11084820A074FD89883CF51BC1A", 16), new BigInteger("04D3938900C0A9AAA7080D1DFEB56CFB0FADABE4214536C7ED5117ED13A", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("0A797F3B8AEFCE7456202DF1E46CCC291EA5A49DA3D4BDDA9A4B62D5E0D", 16), new BigInteger("01F6F81DA55C22DA4152134C661588F4BD6F82FDBAF0C5877096B070DC2", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("015E85A8D46225DD7E314A1C4289731FC14DECE949349FE535D11043B85", 16), new BigInteger("03F189D37F50493EFD5111A129443A662AB3C6B289129AD8C0CAC85119C", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("03B62A4BF783919098B1E42F496E65F7621F01D1D466C46940F0F132A95", 16), new BigInteger("0F4BE031C6E5239E7DAA014CBBF1ED19425E49DAEB426EC9DF4C28A2E30", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("02F1FEDC57BE203E4C8C6B8C1CEB35E13C1FCD956AB41E3BD4C8A6EFB1F", 16), new BigInteger("05738EC8A8EDEA8E435EE7266AD3EDE1EEFC2CEBE2BE1D614008D5D2951", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("0CCE175124D3586BA7486F7146894C65C2A4A5A1904658E5C7F9DF5FA5D", 16), new BigInteger("08804B456D847ACE5CA86D97BF79FD6335E5B17F6C0D964B5D0036C867E", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("035C3D6DFEEA1CFB29B93BE3FDB91A7B130951770C2690C16833A159677", 16), new BigInteger("0600F7301D12AB376B56D4459774159ADB51F97E282FF384406AFD53A02", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("061602FC8068BFD5FB86027B97455D200EC603057446CCE4D76DB8EF42C", 16), new BigInteger("03396DD0D59C067BB999B422D9883736CF9311DFD6951F91033BD03CA8D", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("07E12CB60FDD614958E8E34B3C12DDFF35D85A9C5800E31EA2CC2EF63B1", 16), new BigInteger("0E8970FD99D836F3CC1C807A2C58760DE6EDAA23705A82B9CB1CE93FECC", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("B-283");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("14510D4BC44F2D26F4553942C98073C1BD35545CEABB5CC138853C5158D2729EA408836", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("201E18D48C6DB3D5D097C4DCE1E25587E1501FC3CF47BDB5B4289D79E273D6A9" +
+                "ACB8285", 16), new BigInteger("151AE05712B024CE617358260774C8CA8B0E7A7E72EF8229BF2ACE7609560CB3" +
+                "0322C4F", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("143E878DDFD4DF40D97B8CD638B3C4706501C2201CF7108F2FB91478C11D6947" +
+                "3246925", 16), new BigInteger("0CBF1B9717FEEA3AABB09D9654110144267098E0E1E8D0289A6211BE0EEDFDD8" +
+                "6A3DB79", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("29FD82497FB3E5CEF65579272138DE59E2B666B8689466572B3B69A172CEE83B" +
+                "E145659", 16), new BigInteger("05A89D9166B40795AF0FE5958201B9C0523E500013CA12B4840EA2BC53F25F9B" +
+                "3CE87C0", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("2F00689C1BFCD2A8C7A41E0DE55AE182E6463A152828EF89FE3525139B660329" +
+                "4E69353", 16), new BigInteger("1744514FE0A37447250C8A329EAAADA81572226CABA16F39270EE5DD03F27B1F" +
+                "665EB5D", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("0DA43A9ADFAA6AD767998A054C6A8F1CF77A562924628D73C62761847AD8286E" +
+                "0D91B47", 16), new BigInteger("1D118733AE2C88357827CAFC6F68ABC25C80C640532925E95CFE66D40F8792F3" +
+                "AC44C42", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey,   new BigInteger("05A408133919F2CDCDBE5E4C14FBC706C1F71BADAFEF41F5DE4EC27272FC1CA9" +
+                "366FBB2", 16), new BigInteger("012966272872C097FEA7BCE64FAB1A81982A773E26F6E4EF7C99969846E67CA9" +
+                "CBE1692", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("08F3824E40C16FF1DDA8DC992776D26F4A5981AB5092956C4FDBB4F1AE0A711E" +
+                "EAA10E5", 16), new BigInteger("0A64B91EFADB213E11483FB61C73E3EF63D3B44EEFC56EA401B99DCC60CC28E9" +
+                "9F0F1FA", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("3597B406F5329D11A79E887847E5EC60861CCBB19EC61F252DB7BD549C699951" +
+                "C182796", 16), new BigInteger("0A6A100B997BC622D91701D9F5C6F6D3815517E577622DA69D3A0E8917C1CBE6" +
+                "3ACD345", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("1BB490926E5A1FDC7C5AA86D0835F9B994EDA315CA408002AF54A298728D422E" +
+                "BF59E4C", 16), new BigInteger("36C682CFC9E2C89A782BFD3A191609D1F0C1910D5FD6981442070393159D65FB" +
+                "CC0A8BA", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("19944AA68F9778C2E3D6E240947613E6DA60EFCE9B9B2C063FF5466D72745B5A" +
+                "0B25BA2", 16), new BigInteger("03F1567B3C5B02DF15C874F0EE22850824693D5ADC4663BAA19E384E550B1DD4" +
+                "1F31EE6", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("B-409");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("0494994CC325B08E7B4CE038BD9436F90B5E59A2C13C3140CD3AE07C04A01FC489F572CE0569A6DB7B8060393DE76330C624177", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey,   new BigInteger("0D8783188E1A540E2022D389E1D35B32F56F8C2BB5636B8ABF7718806B27A713" +
+                "EBAE37F63ECD4B61445CEF5801B62594EF3E982", 16), new BigInteger("03A6B4A80E204DB0DE12E7415C13C9EC091C52935658316B4A0C591216A38791" +
+                "54BEB1712560E346E7EF26517707435B55C3141", 16));
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("0EE4F39ACC2E03CE96C3D9FCBAFA5C22C89053662F8D4117752A9B10F09ADFDA" +
+                "59DB061E247FE5321D6B170EE758ACE1BE4D157", 16), new BigInteger("00A2B83265B456A430A8BF27DCC8A9488B3F126C10F0D6D64BF7B8A218FAAF20" +
+                "E51A295A3AE78F205E5A4A6AE224C3639F1BB34", 16));
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("02D8B1B31E33E74D7EB46C30FDE5AD2CA04EC8FE08FBA0E73BA5E568953AC5EA" +
+                "307C072942238DFC07F4A4D7C7C6A9F86436D17", 16), new BigInteger("079F7D471E6CB73234AF7F7C381D2CE15DE35BAF8BB68393B73235B3A26EC2DF" +
+                "4842CE433FB492D6E074E604D4870024D42189A", 16));
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("07BC638B7E7CE6FEE5E9C64A0F966D722D01BB4BC3F3A35F30D4CDDA92DFC5F7" +
+                "F0B4BBFE8065D9AD452FD77A1914BE3A2440C18", 16), new BigInteger("06D904429850521B28A32CBF55C7C0FDF35DC4E0BDA2552C7BF68A171E970E67" +
+                "88ACC0B9521EACB4796E057C70DD9B95FED5BFB", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("05D178DECAFD2D02A3DA0D8BA1C4C1D95EE083C760DF782193A9F7B4A8BE6FC5" +
+                "C21FD60613BCA65C063A61226E050A680B3ABD4", 16), new BigInteger("013B7581E98F6A63FBBCB3E49BCDA60F816DB230B888506D105DC229600497C3" +
+                "B46588C784BE3AA9343BEF82F7C9C80AEB63C3B", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey, new BigInteger("049F54E7C10D2732B4638473053782C6919218BBEFCEC8B51640FC193E832291" +
+                "F05FA12371E9B448417B3290193F08EE9319195", 16), new BigInteger("0499E267DEC84E02F6F108B10E82172C414F15B1B7364BE8BFD66ADC0C5DE23F" +
+                "EE3DF0D811134C25AFE0E05A6672F98889F28F1", 16));
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("0B1527FFAA7DD7C7E46B628587A5BEC0539A2D04D3CF27C54841C2544E1BBDB4" +
+                "2FDBDAAF8671A4CA86DFD619B1E3732D7BB56F2", 16), new BigInteger("0442C68C044868DF4832C807F1EDDEBF7F5052A64B826FD03451440794063F52" +
+                "B022DF304F47403D4069234CA9EB4C964B37C02", 16));
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("0BB27755B991D6D31757BCBF68CB01225A38E1CFA20F775E861055DD108ED7EA" +
+                "455E4B96B2F6F7CD6C6EC2B3C70C3EDDEB9743B", 16), new BigInteger("0C5BE90980E7F444B5F7A12C9E9AC7A04CA81412822DD5AD1BE7C45D5032555E" +
+                "A070864245CF69266871FEB8CD1B7EDC30EF6D5", 16));
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("04EFEB7098772187907C87B33E0FBBA4584226C50C11E98CA7AAC6986F8D3BE0" +
+                "44E5B52D201A410B852536527724CA5F8CE6549", 16), new BigInteger("09574102FEB3EF87E6D66B94119F5A6062950FF4F902EA1E6BD9E2037F33FF99" +
+                "1E31F5956C23AFE48FCDC557FD6F088C7C9B2B3", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("07E0249C68536AE2AEC2EC30090340DA49E6DC9E9EEC8F85E5AABFB234B6DA7D" +
+                "2E9524028CF821F21C6019770474CC40B01FAF6", 16), new BigInteger("08125B5A03FB44AE81EA46D446130C2A415ECCA265910CA69D55F2453E16CD7B" +
+                "2DFA4E28C50FA8137F9C0C6CEE4CD37ABCCF6D8", 16));
+
+            x9ECParameters = NistNamedCurves.GetByName("B-571");
+            ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+            privKey = new ECPrivateKeyParameters(new BigInteger("028A04857F24C1C082DF0D909C0E72F453F2E2340CCB071F0E389BCA2575DA19" +
+                                                                "124198C57174929AD26E348CF63F78D28021EF5A9BF2D5CBEAF6B7CCB6C4DA82" +
+                                                                "4DD5C82CFB24E11", 16), ecDomainParameters);
+
+            DoTestHMacDetECDsaSample(new Sha1Digest(), privKey, new BigInteger("147D3EB0EDA9F2152DFD014363D6A9CE816D7A1467D326A625FC4AB0C786E1B7" +
+                                                                                "4DDF7CD4D0E99541391B266C704BB6B6E8DCCD27B460802E0867143727AA4155" +
+                                                                                "55454321EFE5CB6", 16),
+                                                                  new BigInteger("17319571CAF533D90D2E78A64060B9C53169AB7FC908947B3EDADC54C79CCF0A" +
+                                                                                  "7920B4C64A4EAB6282AFE9A459677CDA37FD6DD50BEF18709590FE18B923BDF7" +
+                                                                                  "4A66B189A850819", 16));
+
+            DoTestHMacDetECDsaSample(new Sha224Digest(), privKey, new BigInteger("10F4B63E79B2E54E4F4F6A2DBC786D8F4A143ECA7B2AD97810F6472AC6AE2085" +
+                                                                                "3222854553BE1D44A7974599DB7061AE8560DF57F2675BE5F9DD94ABAF3D47F1" +
+                                                                                "582B318E459748B", 16),
+                                                                  new BigInteger("3BBEA07C6B269C2B7FE9AE4DDB118338D0C2F0022920A7F9DCFCB7489594C03B" +
+                                                                                  "536A9900C4EA6A10410007222D3DAE1A96F291C4C9275D75D98EB290DC0EEF17" +
+                                                                                  "6037B2C7A7A39A3", 16));
+
+            DoTestHMacDetECDsaSample(new Sha256Digest(), privKey, new BigInteger("213EF9F3B0CFC4BF996B8AF3A7E1F6CACD2B87C8C63820000800AC787F17EC99" +
+                                                                                "C04BCEDF29A8413CFF83142BB88A50EF8D9A086AF4EB03E97C567500C21D8657" +
+                                                                                "14D832E03C6D054", 16),
+                                                                  new BigInteger("3D32322559B094E20D8935E250B6EC139AC4AAB77920812C119AF419FB62B332" +
+                                                                                  "C8D226C6C9362AE3C1E4AABE19359B8428EA74EC8FBE83C8618C2BCCB6B43FBA" +
+                                                                                  "A0F2CCB7D303945", 16));
+
+            DoTestHMacDetECDsaSample(new Sha384Digest(), privKey, new BigInteger("375D8F49C656A0BBD21D3F54CDA287D853C4BB1849983CD891EF6CD6BB56A62B" +
+                                                                                "687807C16685C2C9BCA2663C33696ACCE344C45F3910B1DF806204FF731ECB28" +
+                                                                                "9C100EF4D1805EC", 16),
+                                                                  new BigInteger("1CDEC6F46DFEEE44BCE71D41C60550DC67CF98D6C91363625AC2553E4368D2DF" +
+                                                                                "B734A8E8C72E118A76ACDB0E58697940A0F3DF49E72894BD799450FC9E550CC0" +
+                                                                                "4B9FF9B0380021C", 16));
+            DoTestHMacDetECDsaSample(new Sha512Digest(), privKey, new BigInteger("1C26F40D940A7EAA0EB1E62991028057D91FEDA0366B606F6C434C361F04E545" +
+                                                                                "A6A51A435E26416F6838FFA260C617E798E946B57215284182BE55F29A355E60" +
+                                                                                "24FE32A47289CF0", 16),
+                                                                  new BigInteger("3691DE4369D921FE94EDDA67CB71FBBEC9A436787478063EB1CC778B3DCDC1C4" +
+                                                                                "162662752D28DEEDF6F32A269C82D1DB80C87CE4D3B662E03AC347806E3F19D1" +
+                                                                                "8D6D4DE7358DF7E", 16));
+
+            DoTestHMacDetECDsaTest(new Sha1Digest(), privKey, new BigInteger("133F5414F2A9BC41466D339B79376038A64D045E5B0F792A98E5A7AA87E0AD01" +
+                "6419E5F8D176007D5C9C10B5FD9E2E0AB8331B195797C0358BA05ECBF24ACE59" +
+                "C5F368A6C0997CC", 16),
+                new BigInteger("3D16743AE9F00F0B1A500F738719C5582550FEB64689DA241665C4CE4F328BA0" +
+                    "E34A7EF527ED13BFA5889FD2D1D214C11EB17D6BC338E05A56F41CAFF1AF7B8D" +
+                    "574DB62EF0D0F21", 16));
+
+            DoTestHMacDetECDsaTest(new Sha224Digest(), privKey, new BigInteger("3048E76506C5C43D92B2E33F62B33E3111CEEB87F6C7DF7C7C01E3CDA28FA5E8" +
+                "BE04B5B23AA03C0C70FEF8F723CBCEBFF0B7A52A3F5C8B84B741B4F6157E69A5" +
+                "FB0524B48F31828", 16),
+                new BigInteger("2C99078CCFE5C82102B8D006E3703E020C46C87C75163A2CD839C885550BA5CB" +
+                    "501AC282D29A1C26D26773B60FBE05AAB62BFA0BA32127563D42F7669C97784C" +
+                    "8897C22CFB4B8FA", 16));
+
+            DoTestHMacDetECDsaTest(new Sha256Digest(), privKey, new BigInteger("184BC808506E11A65D628B457FDA60952803C604CC7181B59BD25AEE1411A66D" +
+                                                                        "12A777F3A0DC99E1190C58D0037807A95E5080FA1B2E5CCAA37B50D401CFFC34" +
+                                                                        "17C005AEE963469", 16),
+                                                                  new BigInteger("27280D45F81B19334DBDB07B7E63FE8F39AC7E9AE14DE1D2A6884D2101850289" +
+                                                                      "D70EE400F26ACA5E7D73F534A14568478E59D00594981ABE6A1BA18554C13EB5" +
+                                                                      "E03921E4DC98333", 16));
+
+            DoTestHMacDetECDsaTest(new Sha384Digest(), privKey, new BigInteger("319EE57912E7B0FAA1FBB145B0505849A89C6DB1EC06EA20A6A7EDE072A6268A" +
+                "F6FD9C809C7E422A5F33C6C3326EAD7402467DF3272A1B2726C1C20975950F0F" +
+                "50D8324578F13EC", 16),
+                new BigInteger("2CF3EA27EADD0612DD2F96F46E89AB894B01A10DF985C5FC099CFFE0EA083EB4" +
+                    "4BE682B08BFE405DAD5F37D0A2C59015BA41027E24B99F8F75A70B6B7385BF39" +
+                    "BBEA02513EB880C", 16));
+            DoTestHMacDetECDsaTest(new Sha512Digest(), privKey, new BigInteger("2AA1888EAB05F7B00B6A784C4F7081D2C833D50794D9FEAF6E22B8BE728A2A90" +
+                "BFCABDC803162020AA629718295A1489EE7ED0ECB8AAA197B9BDFC49D18DDD78" +
+                "FC85A48F9715544", 16),
+                new BigInteger("0AA5371FE5CA671D6ED9665849C37F394FED85D51FEF72DA2B5F28EDFB2C6479" +
+                    "CA63320C19596F5E1101988E2C619E302DD05112F47E8823040CE540CD3E90DC" +
+                    "F41DBC461744EE9", 16));
+
+        }
+
+        private void DoTestHMacDetECDsaSample(IDigest digest, ECPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+        {
+            DoTestHMacDetECDsa(new ECDsaSigner(new HMacDsaKCalculator(digest)), digest, SAMPLE, privKey, r, s);
+        }
+
+        private void DoTestHMacDetECDsaTest(IDigest digest, ECPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+        {
+            DoTestHMacDetECDsa(new ECDsaSigner(new HMacDsaKCalculator(digest)), digest, TEST, privKey, r, s);
+        }
+
+        private void DoTestHMacDetECDsa(IDsa detSigner, IDigest digest, byte[] data, ICipherParameters privKey, BigInteger r, BigInteger s)
+        {
+            byte[] m = new byte[digest.GetDigestSize()];
+
+            digest.BlockUpdate(data, 0, data.Length);
+
+            digest.DoFinal(m, 0);
+
+            detSigner.Init(true, privKey);
+
+            BigInteger[] rs = detSigner.GenerateSignature(m);
+
+            if (!r.Equals(rs[0]))
+            {
+                Fail("r value wrong");
+            }
+            if (!s.Equals(rs[1]))
+            {
+                Fail("s value wrong");
+            }
+        }
+
+        public override string Name
+        {
+            get { return "DeterministicDSA"; }
+        }
+
+        public override void PerformTest()
+        {
+            TestHMacDeterministic();
+            TestECHMacDeterministic();
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new DeterministicDsaTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/GCMTest.cs b/crypto/test/src/crypto/test/GCMTest.cs
index 60630c6e4..943ffdad4 100644
--- a/crypto/test/src/crypto/test/GCMTest.cs
+++ b/crypto/test/src/crypto/test/GCMTest.cs
@@ -9,375 +9,415 @@ using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Modes.Gcm;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Date;
 using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.Crypto.Tests
 {
-	/// <summary>
-	/// Test vectors from "The Galois/Counter Mode of Operation (GCM)", McGrew/Viega, Appendix B
-	/// </summary>
-	[TestFixture]
-	public class GcmTest
-		: SimpleTest
-	{
-		private static readonly string[][] TestVectors = new string[][]
-		{
-			new string[]
-			{
-				"Test Case 1",
-				"00000000000000000000000000000000",
-				"",
-				"",
-				"000000000000000000000000",
-				"",
-				"58e2fccefa7e3061367f1d57a4e7455a",
-			},
-			new string[]
-			{
-				"Test Case 2",
-				"00000000000000000000000000000000",
-				"00000000000000000000000000000000",
-				"",
-				"000000000000000000000000",
-				"0388dace60b6a392f328c2b971b2fe78",
-				"ab6e47d42cec13bdf53a67b21257bddf",
-			},
-			new string[]
-			{
-				"Test Case 3",
-				"feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b391aafd255",
-				"",
-				"cafebabefacedbaddecaf888",
-				"42831ec2217774244b7221b784d0d49c"
-				+ "e3aa212f2c02a4e035c17e2329aca12e"
-				+ "21d514b25466931c7d8f6a5aac84aa05"
-				+ "1ba30b396a0aac973d58e091473f5985",
-				"4d5c2af327cd64a62cf35abd2ba6fab4",
-			},
-			new string[]
-			{
-				"Test Case 4",
-				"feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"cafebabefacedbaddecaf888",
-				"42831ec2217774244b7221b784d0d49c"
-				+ "e3aa212f2c02a4e035c17e2329aca12e"
-				+ "21d514b25466931c7d8f6a5aac84aa05"
-				+ "1ba30b396a0aac973d58e091",
-				"5bc94fbc3221a5db94fae95ae7121a47",
-			},
-			new string[]
-			{
-				"Test Case 5",
-				"feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"cafebabefacedbad",
-				"61353b4c2806934a777ff51fa22a4755"
-				+ "699b2a714fcdc6f83766e5f97b6c7423"
-				+ "73806900e49f24b22b097544d4896b42"
-				+ "4989b5e1ebac0f07c23f4598",
-				"3612d2e79e3b0785561be14aaca2fccb",
-			},
-			new string[]
-			{
-				"Test Case 6",
-				"feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"9313225df88406e555909c5aff5269aa"
-				+ "6a7a9538534f7da1e4c303d2a318a728"
-				+ "c3c0c95156809539fcf0e2429a6b5254"
-				+ "16aedbf5a0de6a57a637b39b",
-				"8ce24998625615b603a033aca13fb894"
-				+ "be9112a5c3a211a8ba262a3cca7e2ca7"
-				+ "01e4a9a4fba43c90ccdcb281d48c7c6f"
-				+ "d62875d2aca417034c34aee5",
-				"619cc5aefffe0bfa462af43c1699d050",
-			},
-			new string[]
-			{
-				"Test Case 7",
-				"00000000000000000000000000000000"
-				+ "0000000000000000",
-				"",
-				"",
-				"000000000000000000000000",
-				"",
-				"cd33b28ac773f74ba00ed1f312572435",
-			},
-			new string[]
-			{
-				"Test Case 8",
-				"00000000000000000000000000000000"
-				+ "0000000000000000",
-				"00000000000000000000000000000000",
-				"",
-				"000000000000000000000000",
-				"98e7247c07f0fe411c267e4384b0f600",
-				"2ff58d80033927ab8ef4d4587514f0fb",
-			},
-			new string[]
-			{
-				"Test Case 9",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b391aafd255",
-				"",
-				"cafebabefacedbaddecaf888",
-				"3980ca0b3c00e841eb06fac4872a2757"
-				+ "859e1ceaa6efd984628593b40ca1e19c"
-				+ "7d773d00c144c525ac619d18c84a3f47"
-				+ "18e2448b2fe324d9ccda2710acade256",
-				"9924a7c8587336bfb118024db8674a14",
-			},
-			new string[]
-			{
-				"Test Case 10",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"cafebabefacedbaddecaf888",
-				"3980ca0b3c00e841eb06fac4872a2757"
-				+ "859e1ceaa6efd984628593b40ca1e19c"
-				+ "7d773d00c144c525ac619d18c84a3f47"
-				+ "18e2448b2fe324d9ccda2710",
-				"2519498e80f1478f37ba55bd6d27618c",
-			},
-			new string[]
-			{
-				"Test Case 11",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"cafebabefacedbad",
-				"0f10f599ae14a154ed24b36e25324db8"
-				+ "c566632ef2bbb34f8347280fc4507057"
-				+ "fddc29df9a471f75c66541d4d4dad1c9"
-				+ "e93a19a58e8b473fa0f062f7",
-				"65dcc57fcf623a24094fcca40d3533f8",
-			},
-			new string[]
-			{
-				"Test Case 12",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"9313225df88406e555909c5aff5269aa"
-				+ "6a7a9538534f7da1e4c303d2a318a728"
-				+ "c3c0c95156809539fcf0e2429a6b5254"
-				+ "16aedbf5a0de6a57a637b39b",
-				"d27e88681ce3243c4830165a8fdcf9ff"
-				+ "1de9a1d8e6b447ef6ef7b79828666e45"
-				+ "81e79012af34ddd9e2f037589b292db3"
-				+ "e67c036745fa22e7e9b7373b",
-				"dcf566ff291c25bbb8568fc3d376a6d9",
-			},
-			new string[]
-			{
-				"Test Case 13",
-				"00000000000000000000000000000000"
-				+ "00000000000000000000000000000000",
-				"",
-				"",
-				"000000000000000000000000",
-				"",
-				"530f8afbc74536b9a963b4f1c4cb738b",
-			},
-			new string[]
-			{
-				"Test Case 14",
-				"00000000000000000000000000000000"
-				+ "00000000000000000000000000000000",
-				"00000000000000000000000000000000",
-				"",
-				"000000000000000000000000",
-				"cea7403d4d606b6e074ec5d3baf39d18",
-				"d0d1c8a799996bf0265b98b5d48ab919",
-			},
-			new string[]
-			{
-				"Test Case 15",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b391aafd255",
-				"",
-				"cafebabefacedbaddecaf888",
-				"522dc1f099567d07f47f37a32a84427d"
-				+ "643a8cdcbfe5c0c97598a2bd2555d1aa"
-				+ "8cb08e48590dbb3da7b08b1056828838"
-				+ "c5f61e6393ba7a0abcc9f662898015ad",
-				"b094dac5d93471bdec1a502270e3cc6c",
-			},
-			new string[]
-			{
-				"Test Case 16",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"cafebabefacedbaddecaf888",
-				"522dc1f099567d07f47f37a32a84427d"
-				+ "643a8cdcbfe5c0c97598a2bd2555d1aa"
-				+ "8cb08e48590dbb3da7b08b1056828838"
-				+ "c5f61e6393ba7a0abcc9f662",
-				"76fc6ece0f4e1768cddf8853bb2d551b",
-			},
-			new string[]
-			{
-				"Test Case 17",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"cafebabefacedbad",
-				"c3762df1ca787d32ae47c13bf19844cb"
-				+ "af1ae14d0b976afac52ff7d79bba9de0"
-				+ "feb582d33934a4f0954cc2363bc73f78"
-				+ "62ac430e64abe499f47c9b1f",
-				"3a337dbf46a792c45e454913fe2ea8f2",
-			},
-			new string[]
-			{
-				"Test Case 18",
-				"feffe9928665731c6d6a8f9467308308"
-				+ "feffe9928665731c6d6a8f9467308308",
-				"d9313225f88406e5a55909c5aff5269a"
-				+ "86a7a9531534f7da2e4c303d8a318a72"
-				+ "1c3c0c95956809532fcf0e2449a6b525"
-				+ "b16aedf5aa0de657ba637b39",
-				"feedfacedeadbeeffeedfacedeadbeef"
-				+ "abaddad2",
-				"9313225df88406e555909c5aff5269aa"
-				+ "6a7a9538534f7da1e4c303d2a318a728"
-				+ "c3c0c95156809539fcf0e2429a6b5254"
-				+ "16aedbf5a0de6a57a637b39b",
-				"5a8def2f0c9e53f1f75d7853659e2a20"
-				+ "eeb2b22aafde6419a058ab4f6f746bf4"
-				+ "0fc0c3b780f244452da3ebf1c5d82cde"
-				+ "a2418997200ef82e44ae7e3f",
-				"a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
-			},
-		};
-
-		public override string Name
-		{
-			get { return "GCM"; }
-		}
-
-		public override void PerformTest()
-		{
-			for (int i = 0; i < TestVectors.Length; ++i)
-			{
-				runTestCase(TestVectors[i]);
-			}
-
-			randomTests();
-		}    
-
-		private void runTestCase(
-			string[] testVector)
-		{
-			for (int macLength = 12; macLength <= 16; ++macLength)
-			{
-				runTestCase(testVector, macLength);
-			}
-		}
-
-		private void runTestCase(
-			string[]	testVector,
-			int			macLength)
-		{
-			int pos = 0;
-			string testName = testVector[pos++];
-			byte[] K = Hex.Decode(testVector[pos++]);
-			byte[] P = Hex.Decode(testVector[pos++]);
-			byte[] A = Hex.Decode(testVector[pos++]);
-			byte[] IV = Hex.Decode(testVector[pos++]);
-			byte[] C = Hex.Decode(testVector[pos++]);
-
-			// For short MAC, take leading bytes
-			byte[] t = Hex.Decode(testVector[pos++]);
-			byte[] T = new byte[macLength];
-			Array.Copy(t, T, T.Length);
-
-			// Default multiplier
-            runTestCase(null, null, testName, K, IV, A, P, C, T);
-
-            runTestCase(new BasicGcmMultiplier(), new BasicGcmMultiplier(), testName, K, IV, A, P, C, T);
-            runTestCase(new Tables8kGcmMultiplier(), new Tables8kGcmMultiplier(), testName, K, IV, A, P, C, T);
-            runTestCase(new Tables64kGcmMultiplier(), new Tables64kGcmMultiplier(), testName, K, IV, A, P, C, T);
-		}
-
-        private void runTestCase(
-			IGcmMultiplier	encM,
-			IGcmMultiplier	decM,
-			string			testName,
+    /// <summary>
+    /// Test vectors from "The Galois/Counter Mode of Operation (GCM)", McGrew/Viega, Appendix B
+    /// </summary>
+    [TestFixture]
+    public class GcmTest
+        : SimpleTest
+    {
+        private static readonly string[][] TestVectors = new string[][]
+        {
+            new string[]
+            {
+                "Test Case 1",
+                "00000000000000000000000000000000",
+                "",
+                "",
+                "000000000000000000000000",
+                "",
+                "58e2fccefa7e3061367f1d57a4e7455a",
+            },
+            new string[]
+            {
+                "Test Case 2",
+                "00000000000000000000000000000000",
+                "00000000000000000000000000000000",
+                "",
+                "000000000000000000000000",
+                "0388dace60b6a392f328c2b971b2fe78",
+                "ab6e47d42cec13bdf53a67b21257bddf",
+            },
+            new string[]
+            {
+                "Test Case 3",
+                "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b391aafd255",
+                "",
+                "cafebabefacedbaddecaf888",
+                "42831ec2217774244b7221b784d0d49c"
+                + "e3aa212f2c02a4e035c17e2329aca12e"
+                + "21d514b25466931c7d8f6a5aac84aa05"
+                + "1ba30b396a0aac973d58e091473f5985",
+                "4d5c2af327cd64a62cf35abd2ba6fab4",
+            },
+            new string[]
+            {
+                "Test Case 4",
+                "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "cafebabefacedbaddecaf888",
+                "42831ec2217774244b7221b784d0d49c"
+                + "e3aa212f2c02a4e035c17e2329aca12e"
+                + "21d514b25466931c7d8f6a5aac84aa05"
+                + "1ba30b396a0aac973d58e091",
+                "5bc94fbc3221a5db94fae95ae7121a47",
+            },
+            new string[]
+            {
+                "Test Case 5",
+                "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "cafebabefacedbad",
+                "61353b4c2806934a777ff51fa22a4755"
+                + "699b2a714fcdc6f83766e5f97b6c7423"
+                + "73806900e49f24b22b097544d4896b42"
+                + "4989b5e1ebac0f07c23f4598",
+                "3612d2e79e3b0785561be14aaca2fccb",
+            },
+            new string[]
+            {
+                "Test Case 6",
+                "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "9313225df88406e555909c5aff5269aa"
+                + "6a7a9538534f7da1e4c303d2a318a728"
+                + "c3c0c95156809539fcf0e2429a6b5254"
+                + "16aedbf5a0de6a57a637b39b",
+                "8ce24998625615b603a033aca13fb894"
+                + "be9112a5c3a211a8ba262a3cca7e2ca7"
+                + "01e4a9a4fba43c90ccdcb281d48c7c6f"
+                + "d62875d2aca417034c34aee5",
+                "619cc5aefffe0bfa462af43c1699d050",
+            },
+            new string[]
+            {
+                "Test Case 7",
+                "00000000000000000000000000000000"
+                + "0000000000000000",
+                "",
+                "",
+                "000000000000000000000000",
+                "",
+                "cd33b28ac773f74ba00ed1f312572435",
+            },
+            new string[]
+            {
+                "Test Case 8",
+                "00000000000000000000000000000000"
+                + "0000000000000000",
+                "00000000000000000000000000000000",
+                "",
+                "000000000000000000000000",
+                "98e7247c07f0fe411c267e4384b0f600",
+                "2ff58d80033927ab8ef4d4587514f0fb",
+            },
+            new string[]
+            {
+                "Test Case 9",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b391aafd255",
+                "",
+                "cafebabefacedbaddecaf888",
+                "3980ca0b3c00e841eb06fac4872a2757"
+                + "859e1ceaa6efd984628593b40ca1e19c"
+                + "7d773d00c144c525ac619d18c84a3f47"
+                + "18e2448b2fe324d9ccda2710acade256",
+                "9924a7c8587336bfb118024db8674a14",
+            },
+            new string[]
+            {
+                "Test Case 10",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "cafebabefacedbaddecaf888",
+                "3980ca0b3c00e841eb06fac4872a2757"
+                + "859e1ceaa6efd984628593b40ca1e19c"
+                + "7d773d00c144c525ac619d18c84a3f47"
+                + "18e2448b2fe324d9ccda2710",
+                "2519498e80f1478f37ba55bd6d27618c",
+            },
+            new string[]
+            {
+                "Test Case 11",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "cafebabefacedbad",
+                "0f10f599ae14a154ed24b36e25324db8"
+                + "c566632ef2bbb34f8347280fc4507057"
+                + "fddc29df9a471f75c66541d4d4dad1c9"
+                + "e93a19a58e8b473fa0f062f7",
+                "65dcc57fcf623a24094fcca40d3533f8",
+            },
+            new string[]
+            {
+                "Test Case 12",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "9313225df88406e555909c5aff5269aa"
+                + "6a7a9538534f7da1e4c303d2a318a728"
+                + "c3c0c95156809539fcf0e2429a6b5254"
+                + "16aedbf5a0de6a57a637b39b",
+                "d27e88681ce3243c4830165a8fdcf9ff"
+                + "1de9a1d8e6b447ef6ef7b79828666e45"
+                + "81e79012af34ddd9e2f037589b292db3"
+                + "e67c036745fa22e7e9b7373b",
+                "dcf566ff291c25bbb8568fc3d376a6d9",
+            },
+            new string[]
+            {
+                "Test Case 13",
+                "00000000000000000000000000000000"
+                + "00000000000000000000000000000000",
+                "",
+                "",
+                "000000000000000000000000",
+                "",
+                "530f8afbc74536b9a963b4f1c4cb738b",
+            },
+            new string[]
+            {
+                "Test Case 14",
+                "00000000000000000000000000000000"
+                + "00000000000000000000000000000000",
+                "00000000000000000000000000000000",
+                "",
+                "000000000000000000000000",
+                "cea7403d4d606b6e074ec5d3baf39d18",
+                "d0d1c8a799996bf0265b98b5d48ab919",
+            },
+            new string[]
+            {
+                "Test Case 15",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b391aafd255",
+                "",
+                "cafebabefacedbaddecaf888",
+                "522dc1f099567d07f47f37a32a84427d"
+                + "643a8cdcbfe5c0c97598a2bd2555d1aa"
+                + "8cb08e48590dbb3da7b08b1056828838"
+                + "c5f61e6393ba7a0abcc9f662898015ad",
+                "b094dac5d93471bdec1a502270e3cc6c",
+            },
+            new string[]
+            {
+                "Test Case 16",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "cafebabefacedbaddecaf888",
+                "522dc1f099567d07f47f37a32a84427d"
+                + "643a8cdcbfe5c0c97598a2bd2555d1aa"
+                + "8cb08e48590dbb3da7b08b1056828838"
+                + "c5f61e6393ba7a0abcc9f662",
+                "76fc6ece0f4e1768cddf8853bb2d551b",
+            },
+            new string[]
+            {
+                "Test Case 17",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "cafebabefacedbad",
+                "c3762df1ca787d32ae47c13bf19844cb"
+                + "af1ae14d0b976afac52ff7d79bba9de0"
+                + "feb582d33934a4f0954cc2363bc73f78"
+                + "62ac430e64abe499f47c9b1f",
+                "3a337dbf46a792c45e454913fe2ea8f2",
+            },
+            new string[]
+            {
+                "Test Case 18",
+                "feffe9928665731c6d6a8f9467308308"
+                + "feffe9928665731c6d6a8f9467308308",
+                "d9313225f88406e5a55909c5aff5269a"
+                + "86a7a9531534f7da2e4c303d8a318a72"
+                + "1c3c0c95956809532fcf0e2449a6b525"
+                + "b16aedf5aa0de657ba637b39",
+                "feedfacedeadbeeffeedfacedeadbeef"
+                + "abaddad2",
+                "9313225df88406e555909c5aff5269aa"
+                + "6a7a9538534f7da1e4c303d2a318a728"
+                + "c3c0c95156809539fcf0e2429a6b5254"
+                + "16aedbf5a0de6a57a637b39b",
+                "5a8def2f0c9e53f1f75d7853659e2a20"
+                + "eeb2b22aafde6419a058ab4f6f746bf4"
+                + "0fc0c3b780f244452da3ebf1c5d82cde"
+                + "a2418997200ef82e44ae7e3f",
+                "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+            },
+        };
+
+        public override string Name
+        {
+            get { return "GCM"; }
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < TestVectors.Length; ++i)
+            {
+                RunTestCase(TestVectors[i]);
+            }
+
+            RandomTests();
+            OutputSizeTests();
+            DoTestExceptions();
+        }
+
+        protected IBlockCipher CreateAesEngine()
+        {
+            return new AesFastEngine();
+        }
+
+        private void DoTestExceptions()
+        {
+            GcmBlockCipher gcm = new GcmBlockCipher(CreateAesEngine());
+
+            try
+            {
+                gcm = new GcmBlockCipher(new DesEngine());
+
+                Fail("incorrect block size not picked up");
+            }
+            catch (ArgumentException)
+            {
+                // expected
+            }
+
+            try
+            {
+                gcm.Init(false, new KeyParameter(new byte[16]));
+
+                Fail("illegal argument not picked up");
+            }
+            catch (ArgumentException)
+            {
+                // expected
+            }
+
+            // TODO
+            //AEADTestUtil.testReset(this, new GCMBlockCipher(createAESEngine()), new GCMBlockCipher(createAESEngine()), new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[16]));
+            //AEADTestUtil.testTampering(this, gcm, new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[16]));
+            //AEADTestUtil.testOutputSizes(this, new GCMBlockCipher(createAESEngine()), new AEADParameters(new KeyParameter(
+            //        new byte[16]), 128, new byte[16]));
+            //AEADTestUtil.testBufferSizeChecks(this, new GCMBlockCipher(createAESEngine()), new AEADParameters(
+            //        new KeyParameter(new byte[16]), 128, new byte[16]));
+        }
+
+        private void RunTestCase(string[] testVector)
+        {
+            for (int macLength = 12; macLength <= 16; ++macLength)
+            {
+                RunTestCase(testVector, macLength);
+            }
+        }
+
+        private void RunTestCase(string[] testVector, int macLength)
+        {
+            int pos = 0;
+            string testName = testVector[pos++];
+            byte[] K = Hex.Decode(testVector[pos++]);
+            byte[] P = Hex.Decode(testVector[pos++]);
+            byte[] A = Hex.Decode(testVector[pos++]);
+            byte[] IV = Hex.Decode(testVector[pos++]);
+            byte[] C = Hex.Decode(testVector[pos++]);
+
+            // For short MAC, take leading bytes
+            byte[] t = Hex.Decode(testVector[pos++]);
+            byte[] T = new byte[macLength];
+            Array.Copy(t, T, T.Length);
+
+            // Default multiplier
+            RunTestCase(null, null, testName, K, IV, A, P, C, T);
+
+            RunTestCase(new BasicGcmMultiplier(), new BasicGcmMultiplier(), testName, K, IV, A, P, C, T);
+            RunTestCase(new Tables8kGcmMultiplier(), new Tables8kGcmMultiplier(), testName, K, IV, A, P, C, T);
+            RunTestCase(new Tables64kGcmMultiplier(), new Tables64kGcmMultiplier(), testName, K, IV, A, P, C, T);
+        }
+
+        private void RunTestCase(
+            IGcmMultiplier	encM,
+            IGcmMultiplier	decM,
+            string			testName,
             byte[]          K,
             byte[]          IV,
             byte[]          A,
             byte[]          P,
-			byte[]			C,
-			byte[]			T)
+            byte[]			C,
+            byte[]			T)
         {
             byte[] fa = new byte[A.Length / 2];
             byte[] la = new byte[A.Length - (A.Length / 2)];
             Array.Copy(A, 0, fa, 0, fa.Length);
             Array.Copy(A, fa.Length, la, 0, la.Length);
 
-            runTestCase(encM, decM, testName + " all initial associated data", K, IV, A, null, P, C, T);
-            runTestCase(encM, decM, testName + " all subsequent associated data", K, IV, null, A, P, C, T);
-            runTestCase(encM, decM, testName + " split associated data", K, IV, fa, la, P, C, T);
+            RunTestCase(encM, decM, testName + " all initial associated data", K, IV, A, null, P, C, T);
+            RunTestCase(encM, decM, testName + " all subsequent associated data", K, IV, null, A, P, C, T);
+            RunTestCase(encM, decM, testName + " split associated data", K, IV, fa, la, P, C, T);
         }
 
-        private void runTestCase(
+        private void RunTestCase(
             IGcmMultiplier  encM,
             IGcmMultiplier  decM,
             string          testName,
@@ -390,132 +430,134 @@ namespace Org.BouncyCastle.Crypto.Tests
             byte[]          T)
         {
             AeadParameters parameters = new AeadParameters(new KeyParameter(K), T.Length * 8, IV, A);
-            GcmBlockCipher encCipher = initCipher(encM, true, parameters);
-			GcmBlockCipher decCipher = initCipher(decM, false, parameters);
-            checkTestCase(encCipher, decCipher, testName, SA, P, C, T);
-            checkTestCase(encCipher, decCipher, testName + " (reused)", SA, P, C, T);
+            GcmBlockCipher encCipher = InitCipher(encM, true, parameters);
+            GcmBlockCipher decCipher = InitCipher(decM, false, parameters);
+            CheckTestCase(encCipher, decCipher, testName, SA, P, C, T);
+            CheckTestCase(encCipher, decCipher, testName + " (reused)", SA, P, C, T);
 
             // Key reuse
-            AeadParameters keyReuseParams = new AeadParameters(null, parameters.MacSize, parameters.GetNonce(), parameters.GetAssociatedText());
+            AeadParameters keyReuseParams = AeadTestUtilities.ReuseKey(parameters);
             encCipher.Init(true, keyReuseParams);
             decCipher.Init(false, keyReuseParams);
-            checkTestCase(encCipher, decCipher, testName + " (key reuse)", SA, P, C, T);
-            checkTestCase(encCipher, decCipher, testName + " (key reuse)", SA, P, C, T);
+            CheckTestCase(encCipher, decCipher, testName + " (key reuse)", SA, P, C, T);
         }
 
-		private GcmBlockCipher initCipher(
-			IGcmMultiplier	m,
-			bool			forEncryption,
-			AeadParameters	parameters)
-		{
-			GcmBlockCipher c = new GcmBlockCipher(new AesFastEngine(), m);
-			c.Init(forEncryption, parameters);
-			return c;
-		}
-
-		private void checkTestCase(
-			GcmBlockCipher	encCipher,
-			GcmBlockCipher	decCipher,
-			string			testName,
+        private GcmBlockCipher InitCipher(
+            IGcmMultiplier	m,
+            bool			forEncryption,
+            AeadParameters	parameters)
+        {
+            GcmBlockCipher c = new GcmBlockCipher(CreateAesEngine(), m);
+            c.Init(forEncryption, parameters);
+            return c;
+        }
+
+        private void CheckTestCase(
+            GcmBlockCipher	encCipher,
+            GcmBlockCipher	decCipher,
+            string			testName,
             byte[]          SA,
             byte[]          P,
-			byte[]			C,
-			byte[]			T)
-		{
-			byte[] enc = new byte[encCipher.GetOutputSize(P.Length)];
+            byte[]			C,
+            byte[]			T)
+        {
+            byte[] enc = new byte[encCipher.GetOutputSize(P.Length)];
             if (SA != null)
             {
                 encCipher.ProcessAadBytes(SA, 0, SA.Length);
             }
             int len = encCipher.ProcessBytes(P, 0, P.Length, enc, 0);
-			len += encCipher.DoFinal(enc, len);
+            len += encCipher.DoFinal(enc, len);
 
-			if (enc.Length != len)
-			{
+            if (enc.Length != len)
+            {
 //				Console.WriteLine("" + enc.Length + "/" + len);
-				Fail("encryption reported incorrect length: " + testName);
-			}
+                Fail("encryption reported incorrect length: " + testName);
+            }
 
-			byte[] mac = encCipher.GetMac();
+            byte[] mac = encCipher.GetMac();
 
-			byte[] data = new byte[P.Length];
-			Array.Copy(enc, data, data.Length);
-			byte[] tail = new byte[enc.Length - P.Length];
-			Array.Copy(enc, P.Length, tail, 0, tail.Length);
+            byte[] data = new byte[P.Length];
+            Array.Copy(enc, data, data.Length);
+            byte[] tail = new byte[enc.Length - P.Length];
+            Array.Copy(enc, P.Length, tail, 0, tail.Length);
 
-			if (!AreEqual(C, data))
-			{
-				Fail("incorrect encrypt in: " + testName);
-			}
+            if (!AreEqual(C, data))
+            {
+                Fail("incorrect encrypt in: " + testName);
+            }
 
-			if (!AreEqual(T, mac))
-			{
-				Fail("GetMac() returned wrong mac in: " + testName);
-			}
+            if (!AreEqual(T, mac))
+            {
+                Fail("GetMac() returned wrong mac in: " + testName);
+            }
 
-			if (!AreEqual(T, tail))
-			{
-				Fail("stream contained wrong mac in: " + testName);
-			}
+            if (!AreEqual(T, tail))
+            {
+                Fail("stream contained wrong mac in: " + testName);
+            }
 
-			byte[] dec = new byte[decCipher.GetOutputSize(enc.Length)];
+            byte[] dec = new byte[decCipher.GetOutputSize(enc.Length)];
             if (SA != null)
             {
                 decCipher.ProcessAadBytes(SA, 0, SA.Length);
             }
             len = decCipher.ProcessBytes(enc, 0, enc.Length, dec, 0);
-			len += decCipher.DoFinal(dec, len);
-			mac = decCipher.GetMac();
-
-			data = new byte[C.Length];
-			Array.Copy(dec, data, data.Length);
-
-			if (!AreEqual(P, data))
-			{
-				Fail("incorrect decrypt in: " + testName);
-			}
-		}
-
-        private void randomTests()
-		{
-			SecureRandom srng = new SecureRandom();
-			for (int i = 0; i < 10; ++i)
-			{
-                randomTest(srng, null);
-                randomTest(srng, new BasicGcmMultiplier()); 
-				randomTest(srng, new Tables8kGcmMultiplier());
-                randomTest(srng, new Tables64kGcmMultiplier()); 
-			}
-		}
-
-		private void randomTest(
-			SecureRandom	srng,
-			IGcmMultiplier	m)
-		{
-			int kLength = 16 + 8 * srng.Next(3);
-			byte[] K = new byte[kLength];
-			srng.NextBytes(K);
-
-			int pLength = srng.Next(65536);
-			byte[] P = new byte[pLength];
-			srng.NextBytes(P);
-
-			int aLength = srng.Next(256);
-			byte[] A = new byte[aLength];
-			srng.NextBytes(A);
+            len += decCipher.DoFinal(dec, len);
+            mac = decCipher.GetMac();
+
+            data = new byte[C.Length];
+            Array.Copy(dec, data, data.Length);
+
+            if (!AreEqual(P, data))
+            {
+                Fail("incorrect decrypt in: " + testName);
+            }
+        }
+
+        private void RandomTests()
+        {
+            SecureRandom srng = new SecureRandom();
+            srng.SetSeed(DateTimeUtilities.CurrentUnixMs());
+            RandomTests(srng, null);
+            RandomTests(srng, new BasicGcmMultiplier());
+            RandomTests(srng, new Tables8kGcmMultiplier());
+            RandomTests(srng, new Tables64kGcmMultiplier());
+        }
+
+        private void RandomTests(SecureRandom srng, IGcmMultiplier m)
+        {
+            for (int i = 0; i < 10; ++i)
+            {
+                RandomTest(srng, m);
+            }
+        }
+
+        private void RandomTest(SecureRandom srng, IGcmMultiplier m)
+        {
+            int kLength = 16 + 8 * srng.Next(3);
+            byte[] K = new byte[kLength];
+            srng.NextBytes(K);
+
+            int pLength = srng.Next(65536);
+            byte[] P = new byte[pLength];
+            srng.NextBytes(P);
+
+            int aLength = srng.Next(256);
+            byte[] A = new byte[aLength];
+            srng.NextBytes(A);
 
             int saLength = srng.Next(256);
             byte[] SA = new byte[saLength];
             srng.NextBytes(SA);
 
-			int ivLength = 1 + srng.Next(256);
-			byte[] IV = new byte[ivLength];
-			srng.NextBytes(IV);
+            int ivLength = 1 + srng.Next(256);
+            byte[] IV = new byte[ivLength];
+            srng.NextBytes(IV);
 
-			GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine(), m);
-			AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, IV, A);
-			cipher.Init(true, parameters);
-			byte[] C = new byte[cipher.GetOutputSize(P.Length)];
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, IV, A);
+            GcmBlockCipher cipher = InitCipher(m, true, parameters);
+            byte[] C = new byte[cipher.GetOutputSize(P.Length)];
             int predicted = cipher.GetUpdateOutputSize(P.Length);
 
             int split = srng.Next(SA.Length + 1);
@@ -530,22 +572,22 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             len += cipher.DoFinal(C, len);
 
-			if (C.Length != len)
-			{
-				Fail("encryption reported incorrect length in randomised test");
-			}
+            if (C.Length != len)
+            {
+                Fail("encryption reported incorrect length in randomised test");
+            }
 
-			byte[] encT = cipher.GetMac();
-			byte[] tail = new byte[C.Length - P.Length];
-			Array.Copy(C, P.Length, tail, 0, tail.Length);
+            byte[] encT = cipher.GetMac();
+            byte[] tail = new byte[C.Length - P.Length];
+            Array.Copy(C, P.Length, tail, 0, tail.Length);
 
-			if (!AreEqual(encT, tail))
-			{
-				Fail("stream contained wrong mac in randomised test");
-			}
+            if (!AreEqual(encT, tail))
+            {
+                Fail("stream contained wrong mac in randomised test");
+            }
 
-			cipher.Init(false, parameters);
-			byte[] decP = new byte[cipher.GetOutputSize(C.Length)];
+            cipher.Init(false, parameters);
+            byte[] decP = new byte[cipher.GetOutputSize(C.Length)];
             predicted = cipher.GetUpdateOutputSize(C.Length);
 
             split = srng.Next(SA.Length + 1);
@@ -560,30 +602,110 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             len += cipher.DoFinal(decP, len);
 
-			if (!AreEqual(P, decP))
-			{
-				Fail("incorrect decrypt in randomised test");
-			}
-
-			byte[] decT = cipher.GetMac();
-			if (!AreEqual(encT, decT))
-			{
-				Fail("decryption produced different mac from encryption");
-			}
-		}
-
-		public static void Main(
-			string[] args)
-		{
-			RunTest(new GcmTest());
-		}
-
-		[Test]
-		public void TestFunction()
-		{
-			string resultText = Perform().ToString();
-
-			Assert.AreEqual(Name + ": Okay", resultText);
-		}
-	}
+            if (!AreEqual(P, decP))
+            {
+                Fail("incorrect decrypt in randomised test");
+            }
+
+            byte[] decT = cipher.GetMac();
+            if (!AreEqual(encT, decT))
+            {
+                Fail("decryption produced different mac from encryption");
+            }
+
+            //
+            // key reuse test
+            //
+            cipher.Init(false, AeadTestUtilities.ReuseKey(parameters));
+            decP = new byte[cipher.GetOutputSize(C.Length)];
+
+            split = NextInt(srng, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            len = cipher.ProcessBytes(C, 0, C.Length, decP, 0);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            len += cipher.DoFinal(decP, len);
+
+            if (!AreEqual(P, decP))
+            {
+                Fail("incorrect decrypt in randomised test");
+            }
+
+            decT = cipher.GetMac();
+            if (!AreEqual(encT, decT))
+            {
+                Fail("decryption produced different mac from encryption");
+            }
+        }
+
+        private void OutputSizeTests()
+        {
+            byte[] K = new byte[16];
+            byte[] A = null;
+            byte[] IV = new byte[16];
+
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, IV, A);
+            GcmBlockCipher cipher = InitCipher(null, true, parameters);
+
+            if (cipher.GetUpdateOutputSize(0) != 0)
+            {
+                Fail("incorrect getUpdateOutputSize for initial 0 bytes encryption");
+            }
+
+            if (cipher.GetOutputSize(0) != 16)
+            {
+                Fail("incorrect getOutputSize for initial 0 bytes encryption");
+            }
+
+            cipher.Init(false, parameters);
+
+            if (cipher.GetUpdateOutputSize(0) != 0)
+            {
+                Fail("incorrect getUpdateOutputSize for initial 0 bytes decryption");
+            }
+
+            // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here
+            if (cipher.GetOutputSize(0) != 0)
+            {
+                Fail("fragile getOutputSize for initial 0 bytes decryption");
+            }
+
+            if (cipher.GetOutputSize(16) != 0)
+            {
+                Fail("incorrect getOutputSize for initial MAC-size bytes decryption");
+            }
+        }
+
+        private static int NextInt(SecureRandom rand, int n)
+        {
+            if ((n & -n) == n)  // i.e., n is a power of 2
+            {
+                return (int)(((uint)n * (ulong)((uint)rand.NextInt() >> 1)) >> 31);
+            }
+
+            int bits, value;
+            do
+            {
+                bits = (int)((uint)rand.NextInt() >> 1);
+                value = bits % n;
+            }
+            while (bits - value + (n - 1) < 0);
+
+            return value;
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new GcmTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
 }
diff --git a/crypto/test/src/crypto/test/GMacTest.cs b/crypto/test/src/crypto/test/GMacTest.cs
index 0f0e84e2a..383ff96b7 100644
--- a/crypto/test/src/crypto/test/GMacTest.cs
+++ b/crypto/test/src/crypto/test/GMacTest.cs
@@ -11,174 +11,177 @@ using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.Crypto.Tests
 {
-	/**
-	 * Test vectors for AES-GMAC, extracted from <a
-	 * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test
-	 * vectors</a>.
-	 * 
-	 */
-	[TestFixture]
-	public class GMacTest 
-		: SimpleTest
-	{
-		private class TestCase
-		{
-			private byte[] key;
-			private byte[] iv;
-			private byte[] ad;
-			private byte[] tag;
-			private string name;
-
-			internal TestCase(string name, string key, string iv, string ad, string tag)
-			{
-				this.name = name;
-				this.key = Hex.Decode(key);
-				this.iv = Hex.Decode(iv);
-				this.ad = Hex.Decode(ad);
-				this.tag = Hex.Decode(tag);
-			}
-
-			public string getName()
-			{
-				return name;
-			}
-
-			public byte[] getKey()
-			{
-				return key;
-			}
-
-			public byte[] getIv()
-			{
-				return iv;
-			}
-
-			public byte[] getAd()
-			{
-				return ad;
-			}
-
-			public byte[] getTag()
-			{
-				return tag;
-			}
-		}
-
-		private TestCase[] TEST_VECTORS = new TestCase[] {
-			// Count = 0, from each of the PTlen = 0 test vector sequences
-			new TestCase("128/96/0/128", "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "",
-			             "250327c674aaf477aef2675748cf6971"),
-			new TestCase("128/96/0/120", "272f16edb81a7abbea887357a58c1917", "794ec588176c703d3d2a7a07", "",
-			             "b6e6f197168f5049aeda32dafbdaeb"),
-			new TestCase("128/96/0/112", "81b6844aab6a568c4556a2eb7eae752f", "ce600f59618315a6829bef4d", "",
-			             "89b43e9dbc1b4f597dbbc7655bb5"),
-			new TestCase("128/96/0/104", "cde2f9a9b1a004165ef9dc981f18651b", "29512c29566c7322e1e33e8e", "",
-			             "2e58ce7dabd107c82759c66a75"),
-			new TestCase("128/96/0/96", "b01e45cc3088aaba9fa43d81d481823f", "5a2c4a66468713456a4bd5e1", "",
-			             "014280f944f53c681164b2ff"),
-
-			new TestCase("128/96/128/128", "77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3",
-			             "7a43ec1d9c0a5a78a0b16533a6213cab", "209fcc8d3675ed938e9c7166709dd946"),
-			new TestCase("128/96/128/96", "bea48ae4980d27f357611014d4486625", "32bddb5c3aa998a08556454c",
-			             "8a50b0b8c7654bced884f7f3afda2ead", "8e0f6d8bf05ffebe6f500eb1"),
-
-			new TestCase("128/96/384/128", "99e3e8793e686e571d8285c564f75e2b", "c2dd0ab868da6aa8ad9c0d23",
-			             "b668e42d4e444ca8b23cfdd95a9fedd5178aa521144890b093733cf5cf22526c5917ee476541809ac6867a8c399309fc",
-			             "3f4fba100eaf1f34b0baadaae9995d85"),
-			new TestCase("128/96/384/96", "c77acd1b0918e87053cb3e51651e7013", "39ff857a81745d10f718ac00",
-			             "407992f82ea23b56875d9a3cb843ceb83fd27cb954f7c5534d58539fe96fb534502a1b38ea4fac134db0a42de4be1137",
-			             "2a5dc173285375dc82835876"),
-
-			new TestCase(
-				"128/1024/0/128",
-				"d0f1f4defa1e8c08b4b26d576392027c",
-				"42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac",
-				"", "7ab49b57ddf5f62c427950111c5c4f0d"),
-			new TestCase(
-				"128/1024/384/96",
-				"3cce72d37933394a8cac8a82deada8f0",
-				"aa2f0d676d705d9733c434e481972d4888129cf7ea55c66511b9c0d25a92a174b1e28aa072f27d4de82302828955aadcb817c4907361869bd657b45ff4a6f323871987fcf9413b0702d46667380cd493ed24331a28b9ce5bbfa82d3a6e7679fcce81254ba64abcad14fd18b22c560a9d2c1cd1d3c42dac44c683edf92aced894",
-				"5686b458e9c176f4de8428d9ebd8e12f569d1c7595cf49a4b0654ab194409f86c0dd3fdb8eb18033bb4338c70f0b97d1",
-				"a3a9444b21f330c3df64c8b6"), };
-
-		public override void PerformTest()
-		{
-			for (int i = 0; i < TEST_VECTORS.Length; i++)
-			{
-				TestCase testCase = TEST_VECTORS[i];
-
-				IMac mac = new GMac(new GcmBlockCipher(new AesFastEngine()), testCase.getTag().Length * 8);
-				ICipherParameters key = new KeyParameter(testCase.getKey());
-				mac.Init(new ParametersWithIV(key, testCase.getIv()));
-
-				testSingleByte(mac, testCase);
-				testMultibyte(mac, testCase);
-			}
-
-			// Invalid mac size
-			testInvalidMacSize(97);
-			testInvalidMacSize(136);
-			testInvalidMacSize(88);
-			testInvalidMacSize(64);
-		}
-
-		private void testInvalidMacSize(int size)
-		{
-			try
-			{
-				GMac mac = new GMac(new GcmBlockCipher(new AesFastEngine()), size);
-				mac.Init(new ParametersWithIV(null, new byte[16]));
-				Fail("Expected failure for illegal mac size " + size);
-			}
-			catch (ArgumentException)
-			{
-			}
-		}
-
-		private void testMultibyte(IMac mac, TestCase testCase)
-		{
-			mac.BlockUpdate(testCase.getAd(), 0, testCase.getAd().Length);
-			checkMac(mac, testCase);
-		}
-
-		private void testSingleByte(IMac mac, TestCase testCase)
-		{
-			byte[] ad = testCase.getAd();
-			for (int i = 0; i < ad.Length; i++)
-			{
-				mac.Update(ad[i]);
-			}
-			checkMac(mac, testCase);
-		}
-
-		private void checkMac(IMac mac, TestCase testCase)
-		{
-			byte[] generatedMac = new byte[mac.GetMacSize()];
-			mac.DoFinal(generatedMac, 0);
-			if (!AreEqual(testCase.getTag(), generatedMac))
-			{
-				Fail("Failed " + testCase.getName() + " - expected " + Hex.ToHexString(testCase.getTag()) + " got "
-				     + Hex.ToHexString(generatedMac));
-			}
-		}
-
-		public override string Name
-		{
-			get { return "GMac"; }
-		}
-
-		public static void Main(
-			string[] args)
-		{
-			RunTest(new GMacTest());
-		}
-
-		[Test]
-		public void TestFunction()
-		{
-			string resultText = Perform().ToString();
-
-			Assert.AreEqual(Name + ": Okay", resultText);
-		}
-	}
+    /**
+     * Test vectors for AES-GMAC, extracted from <a
+     * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test
+     * vectors</a>.
+     * 
+     */
+    [TestFixture]
+    public class GMacTest 
+        : SimpleTest
+    {
+        private class TestCase
+        {
+            private byte[] key;
+            private byte[] iv;
+            private byte[] ad;
+            private byte[] tag;
+            private string name;
+
+            internal TestCase(string name, string key, string iv, string ad, string tag)
+            {
+                this.name = name;
+                this.key = Hex.Decode(key);
+                this.iv = Hex.Decode(iv);
+                this.ad = Hex.Decode(ad);
+                this.tag = Hex.Decode(tag);
+            }
+
+            public string getName()
+            {
+                return name;
+            }
+
+            public byte[] getKey()
+            {
+                return key;
+            }
+
+            public byte[] getIv()
+            {
+                return iv;
+            }
+
+            public byte[] getAd()
+            {
+                return ad;
+            }
+
+            public byte[] getTag()
+            {
+                return tag;
+            }
+        }
+
+        private TestCase[] TEST_VECTORS = new TestCase[] {
+            // Count = 0, from each of the PTlen = 0 test vector sequences
+            new TestCase("128/96/0/128", "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "",
+                         "250327c674aaf477aef2675748cf6971"),
+            new TestCase("128/96/0/120", "272f16edb81a7abbea887357a58c1917", "794ec588176c703d3d2a7a07", "",
+                         "b6e6f197168f5049aeda32dafbdaeb"),
+            new TestCase("128/96/0/112", "81b6844aab6a568c4556a2eb7eae752f", "ce600f59618315a6829bef4d", "",
+                         "89b43e9dbc1b4f597dbbc7655bb5"),
+            new TestCase("128/96/0/104", "cde2f9a9b1a004165ef9dc981f18651b", "29512c29566c7322e1e33e8e", "",
+                         "2e58ce7dabd107c82759c66a75"),
+            new TestCase("128/96/0/96", "b01e45cc3088aaba9fa43d81d481823f", "5a2c4a66468713456a4bd5e1", "",
+                         "014280f944f53c681164b2ff"),
+
+            new TestCase("128/96/128/128", "77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3",
+                         "7a43ec1d9c0a5a78a0b16533a6213cab", "209fcc8d3675ed938e9c7166709dd946"),
+            new TestCase("128/96/128/96", "bea48ae4980d27f357611014d4486625", "32bddb5c3aa998a08556454c",
+                         "8a50b0b8c7654bced884f7f3afda2ead", "8e0f6d8bf05ffebe6f500eb1"),
+
+            new TestCase("128/96/384/128", "99e3e8793e686e571d8285c564f75e2b", "c2dd0ab868da6aa8ad9c0d23",
+                         "b668e42d4e444ca8b23cfdd95a9fedd5178aa521144890b093733cf5cf22526c5917ee476541809ac6867a8c399309fc",
+                         "3f4fba100eaf1f34b0baadaae9995d85"),
+            new TestCase("128/96/384/96", "c77acd1b0918e87053cb3e51651e7013", "39ff857a81745d10f718ac00",
+                         "407992f82ea23b56875d9a3cb843ceb83fd27cb954f7c5534d58539fe96fb534502a1b38ea4fac134db0a42de4be1137",
+                         "2a5dc173285375dc82835876"),
+
+            new TestCase(
+                "128/1024/0/128",
+                "d0f1f4defa1e8c08b4b26d576392027c",
+                "42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac",
+                "", "7ab49b57ddf5f62c427950111c5c4f0d"),
+            new TestCase(
+                "128/1024/384/96",
+                "3cce72d37933394a8cac8a82deada8f0",
+                "aa2f0d676d705d9733c434e481972d4888129cf7ea55c66511b9c0d25a92a174b1e28aa072f27d4de82302828955aadcb817c4907361869bd657b45ff4a6f323871987fcf9413b0702d46667380cd493ed24331a28b9ce5bbfa82d3a6e7679fcce81254ba64abcad14fd18b22c560a9d2c1cd1d3c42dac44c683edf92aced894",
+                "5686b458e9c176f4de8428d9ebd8e12f569d1c7595cf49a4b0654ab194409f86c0dd3fdb8eb18033bb4338c70f0b97d1",
+                "a3a9444b21f330c3df64c8b6"), };
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < TEST_VECTORS.Length; i++)
+            {
+                TestCase testCase = TEST_VECTORS[i];
+
+                IMac mac = new GMac(new GcmBlockCipher(new AesFastEngine()), testCase.getTag().Length * 8);
+                ICipherParameters key = new KeyParameter(testCase.getKey());
+                mac.Init(new ParametersWithIV(key, testCase.getIv()));
+
+                testSingleByte(mac, testCase);
+                testMultibyte(mac, testCase);
+            }
+
+            // Invalid mac size
+            testInvalidMacSize(97);
+            testInvalidMacSize(136);
+            testInvalidMacSize(24);
+        }
+
+        private void testInvalidMacSize(int size)
+        {
+            try
+            {
+                GMac mac = new GMac(new GcmBlockCipher(new AesFastEngine()), size);
+                mac.Init(new ParametersWithIV(null, new byte[16]));
+                Fail("Expected failure for illegal mac size " + size);
+            }
+            catch (ArgumentException e)
+            {
+                if (!e.Message.StartsWith("Invalid value for MAC size"))
+                {
+                    Fail("Illegal mac size failed with unexpected message");
+                }
+            }
+        }
+
+        private void testMultibyte(IMac mac, TestCase testCase)
+        {
+            mac.BlockUpdate(testCase.getAd(), 0, testCase.getAd().Length);
+            checkMac(mac, testCase);
+        }
+
+        private void testSingleByte(IMac mac, TestCase testCase)
+        {
+            byte[] ad = testCase.getAd();
+            for (int i = 0; i < ad.Length; i++)
+            {
+                mac.Update(ad[i]);
+            }
+            checkMac(mac, testCase);
+        }
+
+        private void checkMac(IMac mac, TestCase testCase)
+        {
+            byte[] generatedMac = new byte[mac.GetMacSize()];
+            mac.DoFinal(generatedMac, 0);
+            if (!AreEqual(testCase.getTag(), generatedMac))
+            {
+                Fail("Failed " + testCase.getName() + " - expected " + Hex.ToHexString(testCase.getTag()) + " got "
+                     + Hex.ToHexString(generatedMac));
+            }
+        }
+
+        public override string Name
+        {
+            get { return "GMac"; }
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new GMacTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
 }
diff --git a/crypto/test/src/crypto/test/KeccakDigestTest.cs b/crypto/test/src/crypto/test/KeccakDigestTest.cs
new file mode 100644
index 000000000..961a5d2e3
--- /dev/null
+++ b/crypto/test/src/crypto/test/KeccakDigestTest.cs
@@ -0,0 +1,374 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    /**
+     * Keccak Digest Test
+     */
+    [TestFixture]
+    public class KeccakDigestTest
+        : SimpleTest
+    {
+        readonly static string[] messages = {
+            "",
+            "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67",
+            "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"
+        };
+
+        readonly static string[] digests288 = { // the default settings
+            "6753e3380c09e385d0339eb6b050a68f66cfd60a73476e6fd6adeb72f5edd7c6f04a5d01",  // message[0]    
+            "0bbe6afae0d7e89054085c1cc47b1689772c89a41796891e197d1ca1b76f288154933ded",  // message[1]
+            "82558a209b960ddeb531e6dcb281885b2400ca160472462486e79f071e88a3330a8a303d",  // message[2]
+            "94049e1ad7ef5d5b0df2b880489e7ab09ec937c3bfc1b04470e503e1ac7b1133c18f86da",  // 64k a-test
+            "a9cb5a75b5b81b7528301e72553ed6770214fa963956e790528afe420de33c074e6f4220",  // random alphabet test
+            "eadaf5ba2ad6a2f6f338fce0e1efdad2a61bb38f6be6068b01093977acf99e97a5d5827c"   // extremely long data test
+        };
+
+        readonly static string[] digests224 = {
+            "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
+            "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
+            "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab",
+            "f621e11c142fbf35fa8c22841c3a812ba1e0151be4f38d80b9f1ff53",
+            "68b5fc8c87193155bba68a2485377e809ee4f81a85ef023b9e64add0",
+            "c42e4aee858e1a8ad2976896b9d23dd187f64436ee15969afdbc68c5"
+        };
+
+        readonly static string[] digests256 = {
+            "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+            "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
+            "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d",
+            "0047a916daa1f92130d870b542e22d3108444f5a7e4429f05762fb647e6ed9ed",
+            "db368762253ede6d4f1db87e0b799b96e554eae005747a2ea687456ca8bcbd03",
+            "5f313c39963dcf792b5470d4ade9f3a356a3e4021748690a958372e2b06f82a4"
+        };
+
+        readonly static string[] digests384 = {
+            "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
+            "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
+            "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b",
+            "c704cfe7a1a53208ca9526cd24251e0acdc252ecd978eee05acd16425cfb404ea81f5a9e2e5e97784d63ee6a0618a398",
+            "d4fe8586fd8f858dd2e4dee0bafc19b4c12b4e2a856054abc4b14927354931675cdcaf942267f204ea706c19f7beefc4",
+            "9b7168b4494a80a86408e6b9dc4e5a1837c85dd8ff452ed410f2832959c08c8c0d040a892eb9a755776372d4a8732315"
+        };
+
+        readonly static string[] digests512 = {
+            "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
+            "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
+            "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760",
+            "34341ead153aa1d1fdcf6cf624c2b4f6894b6fd16dc38bd4ec971ac0385ad54fafcb2e0ed86a1e509456f4246fdcb02c3172824cd649d9ad54c51f7fb49ea67c",
+            "dc44d4f4d36b07ab5fc04016cbe53548e5a7778671c58a43cb379fd00c06719b8073141fc22191ffc3db5f8b8983ae8341fa37f18c1c969664393aa5ceade64e",
+            "3e122edaf37398231cfaca4c7c216c9d66d5b899ec1d7ac617c40c7261906a45fc01617a021e5da3bd8d4182695b5cb785a28237cbb167590e34718e56d8aab8"
+        };
+
+        // test vectors from  http://www.di-mgt.com.au/hmac_sha3_testvectors.html
+        readonly static byte[][] macKeys =
+        {
+            Hex.Decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
+            Hex.Decode("4a656665"),
+            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+            Hex.Decode("0102030405060708090a0b0c0d0e0f10111213141516171819"),
+            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaa"),
+            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaa"),
+            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+        };
+
+        readonly static string[] macData =
+        {
+            "4869205468657265",
+            "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+            "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +
+                "dddddddddddddddddddddddddddddddddddd",
+            "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" +
+                "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+            "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" +
+                "65204b6579202d2048617368204b6579204669727374",
+            "5468697320697320612074657374207573696e672061206c6172676572207468" +
+                "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+                "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+                "647320746f20626520686173686564206265666f7265206265696e6720757365" +
+                "642062792074686520484d414320616c676f726974686d2e",
+            "5468697320697320612074657374207573696e672061206c6172676572207468" +
+                "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+                "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+                "647320746f20626520686173686564206265666f7265206265696e6720757365\n" +
+                "642062792074686520484d414320616c676f726974686d2e"
+        };
+
+        readonly static string[] mac224 =
+        {
+            "b73d595a2ba9af815e9f2b4e53e78581ebd34a80b3bbaac4e702c4cc",
+            "e824fec96c074f22f99235bb942da1982664ab692ca8501053cbd414",
+            "770df38c99d6e2bacd68056dcfe07d4c89ae20b2686a6185e1faa449",
+            "305a8f2dfb94bad28861a03cbc4d590febe775c58cb4961c28428a0b",
+            "e7a52dfa45f95a217c100066b239aa8ad519be9b35d667268b1b57ff",
+            "ba13009405a929f398b348885caa5419191bb948ada32194afc84104",
+            "92649468be236c3c72c189909c063b13f994be05749dc91310db639e"
+        };
+
+        readonly static string[] mac256 =
+        {
+            "9663d10c73ee294054dc9faf95647cb99731d12210ff7075fb3d3395abfb9821",
+            "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1",
+            "95f43e50f8df80a21977d51a8db3ba572dcd71db24687e6f86f47c1139b26260",
+            "6331ba9b4af5804a68725b3663eb74814494b63c6093e35fb320a85d507936fd",
+            "b4d0cdee7ec2ba81a88b86918958312300a15622377929a054a9ce3ae1fac2b6",
+            "1fdc8cb4e27d07c10d897dec39c217792a6e64fa9c63a77ce42ad106ef284e02",
+            "fdaa10a0299aecff9bb411cf2d7748a4022e4a26be3fb5b11b33d8c2b7ef5484"
+        };
+
+        readonly static string[] mac384 =
+        {
+            "892dfdf5d51e4679bf320cd16d4c9dc6f749744608e003add7fba894acff87361efa4e5799be06b6461f43b60ae97048",
+            "5af5c9a77a23a6a93d80649e562ab77f4f3552e3c5caffd93bdf8b3cfc6920e3023fc26775d9df1f3c94613146ad2c9d",
+            "4243c29f2201992ff96441e3b91ff81d8c601d706fbc83252684a4bc51101ca9b2c06ddd03677303c502ac5331752a3c",
+            "b730724d3d4090cda1be799f63acbbe389fef7792fc18676fa5453aab398664650ed029c3498bbe8056f06c658e1e693",
+            "d62482ef601d7847439b55236e9679388ffcd53c62cd126f39be6ea63de762e26cd5974cb9a8de401b786b5555040f6f",
+            "4860ea191ac34994cf88957afe5a836ef36e4cc1a66d75bf77defb7576122d75f60660e4cf731c6effac06402787e2b9",
+            "fe9357e3cfa538eb0373a2ce8f1e26ad6590afdaf266f1300522e8896d27e73f654d0631c8fa598d4bb82af6b744f4f5"
+        };
+
+        readonly static string[] mac512 =
+        {
+            "8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755",
+            "c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb",
+            "eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66",
+            "b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb",
+            "d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5",
+            "2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572",
+            "6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8"
+        };
+
+        readonly static KeyParameter truncKey = new KeyParameter(Hex.Decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"));
+        readonly static byte[] truncData = Hex.Decode("546573742057697468205472756e636174696f6e");
+
+        readonly static byte[] trunc224 = Hex.Decode("f52bbcfd654264e7133085c5e69b72c3");
+        readonly static byte[] trunc256 = Hex.Decode("745e7e687f8335280d54202ef13cecc6");
+        readonly static byte[] trunc384 = Hex.Decode("fa9aea2bc1e181e47cbb8c3df243814d");
+        readonly static byte[] trunc512 = Hex.Decode("04c929fead434bba190dacfa554ce3f5");
+
+        readonly static byte[] xtremeData = Hex.Decode("61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f");
+
+        public override string Name
+        {
+            get { return "Keccak"; }
+        }
+
+        private void TestDigest(IDigest digest, string[] expected)
+        {
+            byte[] hash = new byte[digest.GetDigestSize()];
+
+            for (int i = 0; i != messages.Length; i++)
+            {
+                if (messages.Length != 0)
+                {
+                    byte[] data = Hex.Decode(messages[i]);
+
+                    digest.BlockUpdate(data, 0, data.Length);
+                }
+
+                digest.DoFinal(hash, 0);
+
+                if (!Arrays.AreEqual(Hex.Decode(expected[i]), hash))
+                {
+                    Fail("Keccak mismatch on " + digest.AlgorithmName + " index " + i);
+                }
+            }
+
+            byte[] k64 = new byte[1024 * 64];
+
+            for (int i = 0; i != k64.Length; i++)
+            {
+                k64[i] = (byte)'a';
+            }
+
+            digest.BlockUpdate(k64, 0, k64.Length);
+
+            digest.DoFinal(hash, 0);
+
+            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length]), hash))
+            {
+                Fail("Keccak mismatch on " + digest.AlgorithmName + " 64k a");
+            }
+
+            for (int i = 0; i != k64.Length; i++)
+            {
+                digest.Update((byte)'a');
+            }
+
+            digest.DoFinal(hash, 0);
+
+            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length]), hash))
+            {
+                Fail("Keccak mismatch on " + digest.AlgorithmName + " 64k a single");
+            }
+
+
+            for (int i = 0; i != k64.Length; i++)
+            {
+                k64[i] = (byte)('a' + (i % 26));
+            }
+
+            digest.BlockUpdate(k64, 0, k64.Length);
+
+            digest.DoFinal(hash, 0);
+
+            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length + 1]), hash))
+            {
+                Fail("Keccak mismatch on " + digest.AlgorithmName + " 64k alpha");
+            }
+
+            for (int i = 0; i != 64; i++)
+            {
+                digest.Update(k64[i * 1024]);
+                digest.BlockUpdate(k64, i * 1024 + 1, 1023);
+            }
+
+            digest.DoFinal(hash, 0);
+
+            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length + 1]), hash))
+            {
+                Fail("Keccak mismatch on " + digest.AlgorithmName + " 64k chunked alpha");
+            }
+
+            TestDigestDoFinal(digest);
+
+            //
+            // extremely long data test
+            //
+            //Console.WriteLine("Starting very long");
+            //for (int i = 0; i != 16384; i++)
+            //{
+            //    for (int j = 0; j != 1024; j++)
+            //    {
+            //        digest.BlockUpdate(xtremeData, 0, xtremeData.Length);
+            //    }
+            //}
+
+            //digest.DoFinal(hash, 0);
+
+            //if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length + 2]), hash))
+            //{
+            //    Fail("Keccak mismatch on " + digest.AlgorithmName + " extreme data test");
+            //}
+            //Console.WriteLine("Done");
+        }
+
+        private void TestDigestDoFinal(IDigest digest)
+        {
+            byte[] hash = new byte[digest.GetDigestSize()];
+            digest.DoFinal(hash, 0);
+
+            for (int i = 0; i <= digest.GetDigestSize(); ++i)
+            {
+                byte[] cmp = new byte[2 * digest.GetDigestSize()];
+                Array.Copy(hash, 0, cmp, i, hash.Length);
+
+                byte[] buf = new byte[2 * digest.GetDigestSize()];
+                digest.DoFinal(buf, i);
+
+                if (!Arrays.AreEqual(cmp, buf))
+                {
+                    Fail("Keccak offset DoFinal on " + digest.AlgorithmName);
+                }
+            }
+        }
+
+        private void TestMac(IDigest digest, byte[][] keys, String[] data, String[] expected, byte[] truncExpected)
+        {
+            IMac mac = new HMac(digest);
+
+            for (int i = 0; i != keys.Length; i++)
+            {
+                mac.Init(new KeyParameter(keys[i]));
+
+                byte[] mData = Hex.Decode(data[i]);
+
+                mac.BlockUpdate(mData, 0, mData.Length);
+
+                byte[] macV = new byte[mac.GetMacSize()];
+
+                mac.DoFinal(macV, 0);
+
+                if (!Arrays.AreEqual(Hex.Decode(expected[i]), macV))
+                {
+                    Fail("Keccak HMAC mismatch on " + digest.AlgorithmName);
+                }
+            }
+
+            {
+                mac = new HMac(digest);
+
+                mac.Init(truncKey);
+
+                mac.BlockUpdate(truncData, 0, truncData.Length);
+
+                byte[] macV = new byte[mac.GetMacSize()];
+
+                mac.DoFinal(macV, 0);
+
+                for (int i = 0; i != truncExpected.Length; i++)
+                {
+                    if (macV[i] != truncExpected[i])
+                    {
+                        Fail("mismatch on truncated HMAC for " + digest.AlgorithmName);
+                    }
+                }
+            }
+        }
+
+        public override void PerformTest()
+        {
+            TestDigest(new KeccakDigest(), digests288);
+            TestDigest(new KeccakDigest(224), digests224);
+            TestDigest(new KeccakDigest(256), digests256);
+            TestDigest(new KeccakDigest(384), digests384);
+            TestDigest(new KeccakDigest(512), digests512);
+
+            TestMac(new KeccakDigest(224), macKeys, macData, mac224, trunc224);
+            TestMac(new KeccakDigest(256), macKeys, macData, mac256, trunc256);
+            TestMac(new KeccakDigest(384), macKeys, macData, mac384, trunc384);
+            TestMac(new KeccakDigest(512), macKeys, macData, mac512, trunc512);
+        }
+
+        protected virtual IDigest CloneDigest(IDigest digest)
+        {
+            return new KeccakDigest((KeccakDigest)digest);
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new KeccakDigestTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/OAEPTest.cs b/crypto/test/src/crypto/test/OAEPTest.cs
index c414f5964..ee48a183d 100644
--- a/crypto/test/src/crypto/test/OAEPTest.cs
+++ b/crypto/test/src/crypto/test/OAEPTest.cs
@@ -17,782 +17,782 @@ using Org.BouncyCastle.X509;
 
 namespace Org.BouncyCastle.Crypto.Tests
 {
-	[TestFixture]
-	public class OaepTest
-		: SimpleTest
-	{
-		private static readonly byte[] pubKeyEnc1 =
-		{
-			(byte)0x30, (byte)0x5a, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
-			(byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
-			(byte)0x00, (byte)0x03, (byte)0x49, (byte)0x00, (byte)0x30, (byte)0x46, (byte)0x02, (byte)0x41,
-			(byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce, (byte)0x88, (byte)0xac, (byte)0xfd,
-			(byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f, (byte)0xc4, (byte)0x52, (byte)0x3f,
-			(byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3, (byte)0x77, (byte)0x4a, (byte)0x25,
-			(byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5, (byte)0xd9, (byte)0x9c, (byte)0xb5,
-			(byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28, (byte)0x5e, (byte)0x53, (byte)0x01,
-			(byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb, (byte)0x68, (byte)0x76, (byte)0x93,
-			(byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62, (byte)0x4a, (byte)0x11, (byte)0xe0,
-			(byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc, (byte)0xac, (byte)0xa0, (byte)0xa1,
-			(byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11
-		};
-
-		private static readonly byte[] privKeyEnc1 =
-		{
-			(byte)0x30, (byte)0x82, (byte)0x01, (byte)0x52, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
-			(byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
-			(byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
-			(byte)0x01, (byte)0x3c, (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x38, (byte)0x02, (byte)0x01,
-			(byte)0x00, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce,
-			(byte)0x88, (byte)0xac, (byte)0xfd, (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f,
-			(byte)0xc4, (byte)0x52, (byte)0x3f, (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3,
-			(byte)0x77, (byte)0x4a, (byte)0x25, (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5,
-			(byte)0xd9, (byte)0x9c, (byte)0xb5, (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28,
-			(byte)0x5e, (byte)0x53, (byte)0x01, (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb,
-			(byte)0x68, (byte)0x76, (byte)0x93, (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62,
-			(byte)0x4a, (byte)0x11, (byte)0xe0, (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc,
-			(byte)0xac, (byte)0xa0, (byte)0xa1, (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11, (byte)0x02,
-			(byte)0x40, (byte)0x0a, (byte)0x03, (byte)0x37, (byte)0x48, (byte)0x62, (byte)0x64, (byte)0x87,
-			(byte)0x69, (byte)0x5f, (byte)0x5f, (byte)0x30, (byte)0xbc, (byte)0x38, (byte)0xb9, (byte)0x8b,
-			(byte)0x44, (byte)0xc2, (byte)0xcd, (byte)0x2d, (byte)0xff, (byte)0x43, (byte)0x40, (byte)0x98,
-			(byte)0xcd, (byte)0x20, (byte)0xd8, (byte)0xa1, (byte)0x38, (byte)0xd0, (byte)0x90, (byte)0xbf,
-			(byte)0x64, (byte)0x79, (byte)0x7c, (byte)0x3f, (byte)0xa7, (byte)0xa2, (byte)0xcd, (byte)0xcb,
-			(byte)0x3c, (byte)0xd1, (byte)0xe0, (byte)0xbd, (byte)0xba, (byte)0x26, (byte)0x54, (byte)0xb4,
-			(byte)0xf9, (byte)0xdf, (byte)0x8e, (byte)0x8a, (byte)0xe5, (byte)0x9d, (byte)0x73, (byte)0x3d,
-			(byte)0x9f, (byte)0x33, (byte)0xb3, (byte)0x01, (byte)0x62, (byte)0x4a, (byte)0xfd, (byte)0x1d,
-			(byte)0x51, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xd8, (byte)0x40, (byte)0xb4, (byte)0x16,
-			(byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4,
-			(byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52,
-			(byte)0x4d, (byte)0x04, (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00,
-			(byte)0xaf, (byte)0x46, (byte)0x12, (byte)0x0d, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xc9,
-			(byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4, (byte)0x53, (byte)0xf6, (byte)0x34,
-			(byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1, (byte)0xd9, (byte)0x35, (byte)0x3f,
-			(byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66, (byte)0xb1, (byte)0xd0, (byte)0x5a,
-			(byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x89, (byte)0x02,
-			(byte)0x20, (byte)0x59, (byte)0x0b, (byte)0x95, (byte)0x72, (byte)0xa2, (byte)0xc2, (byte)0xa9,
-			(byte)0xc4, (byte)0x06, (byte)0x05, (byte)0x9d, (byte)0xc2, (byte)0xab, (byte)0x2f, (byte)0x1d,
-			(byte)0xaf, (byte)0xeb, (byte)0x7e, (byte)0x8b, (byte)0x4f, (byte)0x10, (byte)0xa7, (byte)0x54,
-			(byte)0x9e, (byte)0x8e, (byte)0xed, (byte)0xf5, (byte)0xb4, (byte)0xfc, (byte)0xe0, (byte)0x9e,
-			(byte)0x05, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0x8e, (byte)0x3c, (byte)0x05, (byte)0x21,
-			(byte)0xfe, (byte)0x15, (byte)0xe0, (byte)0xea, (byte)0x06, (byte)0xa3, (byte)0x6f, (byte)0xf0,
-			(byte)0xf1, (byte)0x0c, (byte)0x99, (byte)0x52, (byte)0xc3, (byte)0x5b, (byte)0x7a, (byte)0x75,
-			(byte)0x14, (byte)0xfd, (byte)0x32, (byte)0x38, (byte)0xb8, (byte)0x0a, (byte)0xad, (byte)0x52,
-			(byte)0x98, (byte)0x62, (byte)0x8d, (byte)0x51, (byte)0x02, (byte)0x20, (byte)0x36, (byte)0x3f,
-			(byte)0xf7, (byte)0x18, (byte)0x9d, (byte)0xa8, (byte)0xe9, (byte)0x0b, (byte)0x1d, (byte)0x34,
-			(byte)0x1f, (byte)0x71, (byte)0xd0, (byte)0x9b, (byte)0x76, (byte)0xa8, (byte)0xa9, (byte)0x43,
-			(byte)0xe1, (byte)0x1d, (byte)0x10, (byte)0xb2, (byte)0x4d, (byte)0x24, (byte)0x9f, (byte)0x2d,
-			(byte)0xea, (byte)0xfe, (byte)0xf8, (byte)0x0c, (byte)0x18, (byte)0x26
-		};
-
-		private static readonly byte[] output1 = 
-		{ 
-			(byte)0x1b, (byte)0x8f, (byte)0x05, (byte)0xf9, (byte)0xca, (byte)0x1a, (byte)0x79, (byte)0x52,
-			(byte)0x6e, (byte)0x53, (byte)0xf3, (byte)0xcc, (byte)0x51, (byte)0x4f, (byte)0xdb, (byte)0x89,
-			(byte)0x2b, (byte)0xfb, (byte)0x91, (byte)0x93, (byte)0x23, (byte)0x1e, (byte)0x78, (byte)0xb9,
-			(byte)0x92, (byte)0xe6, (byte)0x8d, (byte)0x50, (byte)0xa4, (byte)0x80, (byte)0xcb, (byte)0x52,
-			(byte)0x33, (byte)0x89, (byte)0x5c, (byte)0x74, (byte)0x95, (byte)0x8d, (byte)0x5d, (byte)0x02,
-			(byte)0xab, (byte)0x8c, (byte)0x0f, (byte)0xd0, (byte)0x40, (byte)0xeb, (byte)0x58, (byte)0x44,
-			(byte)0xb0, (byte)0x05, (byte)0xc3, (byte)0x9e, (byte)0xd8, (byte)0x27, (byte)0x4a, (byte)0x9d,
-			(byte)0xbf, (byte)0xa8, (byte)0x06, (byte)0x71, (byte)0x40, (byte)0x94, (byte)0x39, (byte)0xd2
-		};
-
-		private static readonly byte[] pubKeyEnc2 =
-		{
-			(byte)0x30, (byte)0x4c, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
-			(byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
-			(byte)0x00, (byte)0x03, (byte)0x3b, (byte)0x00, (byte)0x30, (byte)0x38, (byte)0x02, (byte)0x33,
-			(byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d, (byte)0xfd,
-			(byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78, (byte)0xb8,
-			(byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c, (byte)0x98,
-			(byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b, (byte)0x26,
-			(byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b, (byte)0x69,
-			(byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b, (byte)0xe8,
-			(byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03
-		};
-
-		private static readonly byte[] privKeyEnc2 =
-		{
-			(byte)0x30, (byte)0x82, (byte)0x01, (byte)0x13, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
-			(byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
-			(byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x81,
-			(byte)0xfe, (byte)0x30, (byte)0x81, (byte)0xfb, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02,
-			(byte)0x33, (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d,
-			(byte)0xfd, (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78,
-			(byte)0xb8, (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c,
-			(byte)0x98, (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b,
-			(byte)0x26, (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b,
-			(byte)0x69, (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b,
-			(byte)0xe8, (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03, (byte)0x02,
-			(byte)0x32, (byte)0x6c, (byte)0xaf, (byte)0xbc, (byte)0x60, (byte)0x94, (byte)0xb3, (byte)0xfe,
-			(byte)0x4c, (byte)0x72, (byte)0xb0, (byte)0xb3, (byte)0x32, (byte)0xc6, (byte)0xfb, (byte)0x25,
-			(byte)0xa2, (byte)0xb7, (byte)0x62, (byte)0x29, (byte)0x80, (byte)0x4e, (byte)0x68, (byte)0x65,
-			(byte)0xfc, (byte)0xa4, (byte)0x5a, (byte)0x74, (byte)0xdf, (byte)0x0f, (byte)0x8f, (byte)0xb8,
-			(byte)0x41, (byte)0x3b, (byte)0x52, (byte)0xc0, (byte)0xd0, (byte)0xe5, (byte)0x3d, (byte)0x9b,
-			(byte)0x59, (byte)0x0f, (byte)0xf1, (byte)0x9b, (byte)0xe7, (byte)0x9f, (byte)0x49, (byte)0xdd,
-			(byte)0x21, (byte)0xe5, (byte)0xeb, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0xcf, (byte)0x20,
-			(byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4,
-			(byte)0x16, (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3,
-			(byte)0xb4, (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x91, (byte)0x02,
-			(byte)0x1a, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
-			(byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
-			(byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
-			(byte)0xb1, (byte)0xd0, (byte)0x5f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x8a, (byte)0x15,
-			(byte)0x78, (byte)0xac, (byte)0x5d, (byte)0x13, (byte)0xaf, (byte)0x10, (byte)0x2b, (byte)0x22,
-			(byte)0xb9, (byte)0x99, (byte)0xcd, (byte)0x74, (byte)0x61, (byte)0xf1, (byte)0x5e, (byte)0x6d,
-			(byte)0x22, (byte)0xcc, (byte)0x03, (byte)0x23, (byte)0xdf, (byte)0xdf, (byte)0x0b, (byte)0x02,
-			(byte)0x1a, (byte)0x00, (byte)0x86, (byte)0x55, (byte)0x21, (byte)0x4a, (byte)0xc5, (byte)0x4d,
-			(byte)0x8d, (byte)0x4e, (byte)0xcd, (byte)0x61, (byte)0x77, (byte)0xf1, (byte)0xc7, (byte)0x36,
-			(byte)0x90, (byte)0xce, (byte)0x2a, (byte)0x48, (byte)0x2c, (byte)0x8b, (byte)0x05, (byte)0x99,
-			(byte)0xcb, (byte)0xe0, (byte)0x3f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x83, (byte)0xef,
-			(byte)0xef, (byte)0xb8, (byte)0xa9, (byte)0xa4, (byte)0x0d, (byte)0x1d, (byte)0xb6, (byte)0xed,
-			(byte)0x98, (byte)0xad, (byte)0x84, (byte)0xed, (byte)0x13, (byte)0x35, (byte)0xdc, (byte)0xc1,
-			(byte)0x08, (byte)0xf3, (byte)0x22, (byte)0xd0, (byte)0x57, (byte)0xcf, (byte)0x8d
-		};
-
-		private static readonly byte[] output2 =
-		{
-			(byte)0x14, (byte)0xbd, (byte)0xdd, (byte)0x28, (byte)0xc9, (byte)0x83, (byte)0x35, (byte)0x19,
-			(byte)0x23, (byte)0x80, (byte)0xe8, (byte)0xe5, (byte)0x49, (byte)0xb1, (byte)0x58, (byte)0x2a,
-			(byte)0x8b, (byte)0x40, (byte)0xb4, (byte)0x48, (byte)0x6d, (byte)0x03, (byte)0xa6, (byte)0xa5,
-			(byte)0x31, (byte)0x1f, (byte)0x1f, (byte)0xd5, (byte)0xf0, (byte)0xa1, (byte)0x80, (byte)0xe4,
-			(byte)0x17, (byte)0x53, (byte)0x03, (byte)0x29, (byte)0xa9, (byte)0x34, (byte)0x90, (byte)0x74,
-			(byte)0xb1, (byte)0x52, (byte)0x13, (byte)0x54, (byte)0x29, (byte)0x08, (byte)0x24, (byte)0x52,
-			(byte)0x62, (byte)0x51
-		};
-
-		private static readonly byte[] pubKeyEnc3 =
-		{
-			(byte)0x30, (byte)0x81, (byte)0x9d, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a,
-			(byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01,
-			(byte)0x05, (byte)0x00, (byte)0x03, (byte)0x81, (byte)0x8b, (byte)0x00, (byte)0x30, (byte)0x81,
-			(byte)0x87, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
-			(byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
-			(byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
-			(byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
-			(byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
-			(byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
-			(byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
-			(byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
-			(byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
-			(byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
-			(byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
-			(byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
-			(byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
-			(byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
-			(byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
-			(byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
-			(byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11
-		};
-
-		private static readonly byte[] privKeyEnc3 =
-		{
-			(byte)0x30, (byte)0x82, (byte)0x02, (byte)0x75, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
-			(byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
-			(byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
-			(byte)0x02, (byte)0x5f, (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x5b, (byte)0x02, (byte)0x01,
-			(byte)0x00, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
-			(byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
-			(byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
-			(byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
-			(byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
-			(byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
-			(byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
-			(byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
-			(byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
-			(byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
-			(byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
-			(byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
-			(byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
-			(byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
-			(byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
-			(byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
-			(byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11,
-			(byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xa5, (byte)0xda, (byte)0xfc, (byte)0x53,
-			(byte)0x41, (byte)0xfa, (byte)0xf2, (byte)0x89, (byte)0xc4, (byte)0xb9, (byte)0x88, (byte)0xdb,
-			(byte)0x30, (byte)0xc1, (byte)0xcd, (byte)0xf8, (byte)0x3f, (byte)0x31, (byte)0x25, (byte)0x1e,
-			(byte)0x06, (byte)0x68, (byte)0xb4, (byte)0x27, (byte)0x84, (byte)0x81, (byte)0x38, (byte)0x01,
-			(byte)0x57, (byte)0x96, (byte)0x41, (byte)0xb2, (byte)0x94, (byte)0x10, (byte)0xb3, (byte)0xc7,
-			(byte)0x99, (byte)0x8d, (byte)0x6b, (byte)0xc4, (byte)0x65, (byte)0x74, (byte)0x5e, (byte)0x5c,
-			(byte)0x39, (byte)0x26, (byte)0x69, (byte)0xd6, (byte)0x87, (byte)0x0d, (byte)0xa2, (byte)0xc0,
-			(byte)0x82, (byte)0xa9, (byte)0x39, (byte)0xe3, (byte)0x7f, (byte)0xdc, (byte)0xb8, (byte)0x2e,
-			(byte)0xc9, (byte)0x3e, (byte)0xda, (byte)0xc9, (byte)0x7f, (byte)0xf3, (byte)0xad, (byte)0x59,
-			(byte)0x50, (byte)0xac, (byte)0xcf, (byte)0xbc, (byte)0x11, (byte)0x1c, (byte)0x76, (byte)0xf1,
-			(byte)0xa9, (byte)0x52, (byte)0x94, (byte)0x44, (byte)0xe5, (byte)0x6a, (byte)0xaf, (byte)0x68,
-			(byte)0xc5, (byte)0x6c, (byte)0x09, (byte)0x2c, (byte)0xd3, (byte)0x8d, (byte)0xc3, (byte)0xbe,
-			(byte)0xf5, (byte)0xd2, (byte)0x0a, (byte)0x93, (byte)0x99, (byte)0x26, (byte)0xed, (byte)0x4f,
-			(byte)0x74, (byte)0xa1, (byte)0x3e, (byte)0xdd, (byte)0xfb, (byte)0xe1, (byte)0xa1, (byte)0xce,
-			(byte)0xcc, (byte)0x48, (byte)0x94, (byte)0xaf, (byte)0x94, (byte)0x28, (byte)0xc2, (byte)0xb7,
-			(byte)0xb8, (byte)0x88, (byte)0x3f, (byte)0xe4, (byte)0x46, (byte)0x3a, (byte)0x4b, (byte)0xc8,
-			(byte)0x5b, (byte)0x1c, (byte)0xb3, (byte)0xc1, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xee,
-			(byte)0xcf, (byte)0xae, (byte)0x81, (byte)0xb1, (byte)0xb9, (byte)0xb3, (byte)0xc9, (byte)0x08,
-			(byte)0x81, (byte)0x0b, (byte)0x10, (byte)0xa1, (byte)0xb5, (byte)0x60, (byte)0x01, (byte)0x99,
-			(byte)0xeb, (byte)0x9f, (byte)0x44, (byte)0xae, (byte)0xf4, (byte)0xfd, (byte)0xa4, (byte)0x93,
-			(byte)0xb8, (byte)0x1a, (byte)0x9e, (byte)0x3d, (byte)0x84, (byte)0xf6, (byte)0x32, (byte)0x12,
-			(byte)0x4e, (byte)0xf0, (byte)0x23, (byte)0x6e, (byte)0x5d, (byte)0x1e, (byte)0x3b, (byte)0x7e,
-			(byte)0x28, (byte)0xfa, (byte)0xe7, (byte)0xaa, (byte)0x04, (byte)0x0a, (byte)0x2d, (byte)0x5b,
-			(byte)0x25, (byte)0x21, (byte)0x76, (byte)0x45, (byte)0x9d, (byte)0x1f, (byte)0x39, (byte)0x75,
-			(byte)0x41, (byte)0xba, (byte)0x2a, (byte)0x58, (byte)0xfb, (byte)0x65, (byte)0x99, (byte)0x02,
-			(byte)0x41, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
-			(byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
-			(byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
-			(byte)0xb1, (byte)0xd0, (byte)0x5a, (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b,
-			(byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4, (byte)0x16, (byte)0x66, (byte)0xb4,
-			(byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4, (byte)0x32, (byte)0x04,
-			(byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52, (byte)0x4d, (byte)0x04,
-			(byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00, (byte)0xaf, (byte)0x46,
-			(byte)0x15, (byte)0x03, (byte)0x02, (byte)0x40, (byte)0x54, (byte)0x49, (byte)0x4c, (byte)0xa6,
-			(byte)0x3e, (byte)0xba, (byte)0x03, (byte)0x37, (byte)0xe4, (byte)0xe2, (byte)0x40, (byte)0x23,
-			(byte)0xfc, (byte)0xd6, (byte)0x9a, (byte)0x5a, (byte)0xeb, (byte)0x07, (byte)0xdd, (byte)0xdc,
-			(byte)0x01, (byte)0x83, (byte)0xa4, (byte)0xd0, (byte)0xac, (byte)0x9b, (byte)0x54, (byte)0xb0,
-			(byte)0x51, (byte)0xf2, (byte)0xb1, (byte)0x3e, (byte)0xd9, (byte)0x49, (byte)0x09, (byte)0x75,
-			(byte)0xea, (byte)0xb7, (byte)0x74, (byte)0x14, (byte)0xff, (byte)0x59, (byte)0xc1, (byte)0xf7,
-			(byte)0x69, (byte)0x2e, (byte)0x9a, (byte)0x2e, (byte)0x20, (byte)0x2b, (byte)0x38, (byte)0xfc,
-			(byte)0x91, (byte)0x0a, (byte)0x47, (byte)0x41, (byte)0x74, (byte)0xad, (byte)0xc9, (byte)0x3c,
-			(byte)0x1f, (byte)0x67, (byte)0xc9, (byte)0x81, (byte)0x02, (byte)0x40, (byte)0x47, (byte)0x1e,
-			(byte)0x02, (byte)0x90, (byte)0xff, (byte)0x0a, (byte)0xf0, (byte)0x75, (byte)0x03, (byte)0x51,
-			(byte)0xb7, (byte)0xf8, (byte)0x78, (byte)0x86, (byte)0x4c, (byte)0xa9, (byte)0x61, (byte)0xad,
-			(byte)0xbd, (byte)0x3a, (byte)0x8a, (byte)0x7e, (byte)0x99, (byte)0x1c, (byte)0x5c, (byte)0x05,
-			(byte)0x56, (byte)0xa9, (byte)0x4c, (byte)0x31, (byte)0x46, (byte)0xa7, (byte)0xf9, (byte)0x80,
-			(byte)0x3f, (byte)0x8f, (byte)0x6f, (byte)0x8a, (byte)0xe3, (byte)0x42, (byte)0xe9, (byte)0x31,
-			(byte)0xfd, (byte)0x8a, (byte)0xe4, (byte)0x7a, (byte)0x22, (byte)0x0d, (byte)0x1b, (byte)0x99,
-			(byte)0xa4, (byte)0x95, (byte)0x84, (byte)0x98, (byte)0x07, (byte)0xfe, (byte)0x39, (byte)0xf9,
-			(byte)0x24, (byte)0x5a, (byte)0x98, (byte)0x36, (byte)0xda, (byte)0x3d, (byte)0x02, (byte)0x41,
-			(byte)0x00, (byte)0xb0, (byte)0x6c, (byte)0x4f, (byte)0xda, (byte)0xbb, (byte)0x63, (byte)0x01,
-			(byte)0x19, (byte)0x8d, (byte)0x26, (byte)0x5b, (byte)0xdb, (byte)0xae, (byte)0x94, (byte)0x23,
-			(byte)0xb3, (byte)0x80, (byte)0xf2, (byte)0x71, (byte)0xf7, (byte)0x34, (byte)0x53, (byte)0x88,
-			(byte)0x50, (byte)0x93, (byte)0x07, (byte)0x7f, (byte)0xcd, (byte)0x39, (byte)0xe2, (byte)0x11,
-			(byte)0x9f, (byte)0xc9, (byte)0x86, (byte)0x32, (byte)0x15, (byte)0x4f, (byte)0x58, (byte)0x83,
-			(byte)0xb1, (byte)0x67, (byte)0xa9, (byte)0x67, (byte)0xbf, (byte)0x40, (byte)0x2b, (byte)0x4e,
-			(byte)0x9e, (byte)0x2e, (byte)0x0f, (byte)0x96, (byte)0x56, (byte)0xe6, (byte)0x98, (byte)0xea,
-			(byte)0x36, (byte)0x66, (byte)0xed, (byte)0xfb, (byte)0x25, (byte)0x79, (byte)0x80, (byte)0x39,
-			(byte)0xf7
-		};
-
-		private static readonly byte[] output3 = Hex.Decode(
-			  "b8246b56a6ed5881aeb585d9a25b2ad790c417e080681bf1ac2bc3deb69d8bce"
-			+ "f0c4366fec400af052a72e9b0effb5b3f2f192dbeaca03c12740057113bf1f06"
-			+ "69ac22e9f3a7852e3c15d913cab0b8863a95c99294ce8674214954610346f4d4"
-			+ "74b26f7c48b42ee68e1f572a1fc4026ac456b4f59f7b621ea1b9d88f64202fb1");
-
-		private static readonly byte[] seed = {
-			(byte)0xaa, (byte)0xfd, (byte)0x12, (byte)0xf6, (byte)0x59,
-			(byte)0xca, (byte)0xe6, (byte)0x34, (byte)0x89, (byte)0xb4,
-			(byte)0x79, (byte)0xe5, (byte)0x07, (byte)0x6d, (byte)0xde,
-			(byte)0xc2, (byte)0xf0, (byte)0x6c, (byte)0xb5, (byte)0x8f
-		};
-
-		private class VecRand
-			: SecureRandom
-		{
-			private readonly byte[] seed;
-
-			internal VecRand(byte[] seed)
-			{
-				this.seed = seed;
-			}
-
-			public override void NextBytes(
-				byte[] bytes)
-			{
-				Array.Copy(seed, 0, bytes, 0, bytes.Length);
-			}
-		}
-
-		private void BaseOaepTest(
-			int		id,
-			byte[]	pubKeyEnc,
-			byte[]	privKeyEnc,
-			byte[]	output)
-		{
-			//
-			// extract the public key info.
-			//
-			Asn1Object pubKeyObj = Asn1Object.FromByteArray(pubKeyEnc);
-			RsaPublicKeyStructure pubStruct = RsaPublicKeyStructure.GetInstance(
-				SubjectPublicKeyInfo.GetInstance(pubKeyObj).GetPublicKey());
-
-			//
-			// extract the private key info.
-			//
-			Asn1Object privKeyObj = Asn1Object.FromByteArray(privKeyEnc);
-			RsaPrivateKeyStructure privStruct = new RsaPrivateKeyStructure(
-				(Asn1Sequence) PrivateKeyInfo.GetInstance(privKeyObj).PrivateKey);
-
-			RsaKeyParameters pubParameters = new RsaKeyParameters(
-				false,
-				pubStruct.Modulus,
-				pubStruct.PublicExponent);
-
-			RsaKeyParameters privParameters = new RsaPrivateCrtKeyParameters(
-				privStruct.Modulus,
-				privStruct.PublicExponent,
-				privStruct.PrivateExponent,
-				privStruct.Prime1,
-				privStruct.Prime2,
-				privStruct.Exponent1,
-				privStruct.Exponent2,
-				privStruct.Coefficient);
-
-			byte[] input = new byte[] {
-				(byte)0x54, (byte)0x85, (byte)0x9b, (byte)0x34,
-				(byte)0x2c, (byte)0x49, (byte)0xea, (byte)0x2a
-			};
-
-			EncDec("id(" + id + ")", pubParameters, privParameters, seed, input, output);
-		}
-
-		private void EncDec(
-			string				label,
-			RsaKeyParameters	pubParameters,
-			RsaKeyParameters	privParameters,
-			byte[]				seed,
-			byte[]				input,
-			byte[]				output)
-		{
-			IAsymmetricBlockCipher cipher = new OaepEncoding(new RsaEngine());
-
-			cipher.Init(true, new ParametersWithRandom(pubParameters, new VecRand(seed)));
-
-			byte[] outBytes = cipher.ProcessBlock(input, 0, input.Length);
-
-			for (int i = 0; i != output.Length; i++)
-			{
-				if (outBytes[i] != output[i])
-				{
-					Fail(label + " failed encryption");
-				}
-			}
-
-			cipher.Init(false, privParameters);
-
-			outBytes = cipher.ProcessBlock(output, 0, output.Length);
-
-			for (int i = 0; i != input.Length; i++)
-			{
-				if (outBytes[i] != input[i])
-				{
-					Fail(label + " failed decoding");
-				}
-			}
-		}
-
-		/*
-		* RSA vector tests from PKCS#1 page
-		*/
-		private static readonly byte[] modulus_1024 = Hex.Decode(
-			  "a8b3b284af8eb50b387034a860f146c4"
-			+ "919f318763cd6c5598c8ae4811a1e0ab"
-			+ "c4c7e0b082d693a5e7fced675cf46685"
-			+ "12772c0cbc64a742c6c630f533c8cc72"
-			+ "f62ae833c40bf25842e984bb78bdbf97"
-			+ "c0107d55bdb662f5c4e0fab9845cb514"
-			+ "8ef7392dd3aaff93ae1e6b667bb3d424"
-			+ "7616d4f5ba10d4cfd226de88d39f16fb");
-
-		private static readonly byte[] pubExp_1024 = Hex.Decode("010001");
-
-		private static readonly byte[] privExp_1024 = Hex.Decode(
-			  "53339cfdb79fc8466a655c7316aca85c"
-			+ "55fd8f6dd898fdaf119517ef4f52e8fd"
-			+ "8e258df93fee180fa0e4ab29693cd83b"
-			+ "152a553d4ac4d1812b8b9fa5af0e7f55"
-			+ "fe7304df41570926f3311f15c4d65a73"
-			+ "2c483116ee3d3d2d0af3549ad9bf7cbf"
-			+ "b78ad884f84d5beb04724dc7369b31de"
-			+ "f37d0cf539e9cfcdd3de653729ead5d1");
-
-		private static readonly byte[] prime1_1024 = Hex.Decode(
-			  "d32737e7267ffe1341b2d5c0d150a81b"
-			+ "586fb3132bed2f8d5262864a9cb9f30a"
-			+ "f38be448598d413a172efb802c21acf1"
-			+ "c11c520c2f26a471dcad212eac7ca39d");
-
-		private static readonly byte[] prime2_1024 = Hex.Decode(
-			  "cc8853d1d54da630fac004f471f281c7"
-			+ "b8982d8224a490edbeb33d3e3d5cc93c"
-			+ "4765703d1dd791642f1f116a0dd852be"
-			+ "2419b2af72bfe9a030e860b0288b5d77");
-
-		private static readonly byte[] primeExp1_1024 = Hex.Decode(
-			  "0e12bf1718e9cef5599ba1c3882fe804"
-			+ "6a90874eefce8f2ccc20e4f2741fb0a3"
-			+ "3a3848aec9c9305fbecbd2d76819967d"
-			+ "4671acc6431e4037968db37878e695c1");
-
-		private static readonly byte[] primeExp2_1024 = Hex.Decode(
-			  "95297b0f95a2fa67d00707d609dfd4fc"
-			+ "05c89dafc2ef6d6ea55bec771ea33373"
-			+ "4d9251e79082ecda866efef13c459e1a"
-			+ "631386b7e354c899f5f112ca85d71583");
-
-		private static readonly byte[] crtCoef_1024 = Hex.Decode(
-			  "4f456c502493bdc0ed2ab756a3a6ed4d"
-			+ "67352a697d4216e93212b127a63d5411"
-			+ "ce6fa98d5dbefd73263e372814274381"
-			+ "8166ed7dd63687dd2a8ca1d2f4fbd8e1");
-
-		private static readonly byte[] input_1024_1 = Hex.Decode(
-			  "6628194e12073db03ba94cda9ef95323"
-			+ "97d50dba79b987004afefe34");
-
-		private static readonly byte[] seed_1024_1 = Hex.Decode(
-			  "18b776ea21069d69776a33e96bad48e1"
-			+ "dda0a5ef");
-
-		private static readonly byte[] output_1024_1 = Hex.Decode(
-			  "354fe67b4a126d5d35fe36c777791a3f"
-			+ "7ba13def484e2d3908aff722fad468fb"
-			+ "21696de95d0be911c2d3174f8afcc201"
-			+ "035f7b6d8e69402de5451618c21a535f"
-			+ "a9d7bfc5b8dd9fc243f8cf927db31322"
-			+ "d6e881eaa91a996170e657a05a266426"
-			+ "d98c88003f8477c1227094a0d9fa1e8c"
-			+ "4024309ce1ecccb5210035d47ac72e8a");
-
-		private static readonly byte[] input_1024_2 = Hex.Decode(
-			  "750c4047f547e8e41411856523298ac9"
-			+ "bae245efaf1397fbe56f9dd5");
-
-		private static readonly byte[] seed_1024_2 = Hex.Decode(
-			  "0cc742ce4a9b7f32f951bcb251efd925"
-			+ "fe4fe35f");
-
-		private static readonly byte[] output_1024_2 = Hex.Decode(
-			  "640db1acc58e0568fe5407e5f9b701df"
-			+ "f8c3c91e716c536fc7fcec6cb5b71c11"
-			+ "65988d4a279e1577d730fc7a29932e3f"
-			+ "00c81515236d8d8e31017a7a09df4352"
-			+ "d904cdeb79aa583adcc31ea698a4c052"
-			+ "83daba9089be5491f67c1a4ee48dc74b"
-			+ "bbe6643aef846679b4cb395a352d5ed1"
-			+ "15912df696ffe0702932946d71492b44");
-
-		private static readonly byte[] input_1024_3 = Hex.Decode(
-			  "d94ae0832e6445ce42331cb06d531a82"
-			+ "b1db4baad30f746dc916df24d4e3c245"
-			+ "1fff59a6423eb0e1d02d4fe646cf699d"
-			+ "fd818c6e97b051");
-
-		private static readonly byte[] seed_1024_3 = Hex.Decode(
-			  "2514df4695755a67b288eaf4905c36ee"
-			+ "c66fd2fd");
-
-		private static readonly byte[] output_1024_3 = Hex.Decode(
-			  "423736ed035f6026af276c35c0b3741b"
-			+ "365e5f76ca091b4e8c29e2f0befee603"
-			+ "595aa8322d602d2e625e95eb81b2f1c9"
-			+ "724e822eca76db8618cf09c5343503a4"
-			+ "360835b5903bc637e3879fb05e0ef326"
-			+ "85d5aec5067cd7cc96fe4b2670b6eac3"
-			+ "066b1fcf5686b68589aafb7d629b02d8"
-			+ "f8625ca3833624d4800fb081b1cf94eb");
-
-		private static readonly byte[] input_1024_4 = Hex.Decode(
-			  "52e650d98e7f2a048b4f86852153b97e"
-			+ "01dd316f346a19f67a85");
-
-		private static readonly byte[] seed_1024_4 = Hex.Decode(
-			  "c4435a3e1a18a68b6820436290a37cef"
-			+ "b85db3fb");
-
-		private static readonly byte[] output_1024_4 = Hex.Decode(
-			  "45ead4ca551e662c9800f1aca8283b05"
-			+ "25e6abae30be4b4aba762fa40fd3d38e"
-			+ "22abefc69794f6ebbbc05ddbb1121624"
-			+ "7d2f412fd0fba87c6e3acd888813646f"
-			+ "d0e48e785204f9c3f73d6d8239562722"
-			+ "dddd8771fec48b83a31ee6f592c4cfd4"
-			+ "bc88174f3b13a112aae3b9f7b80e0fc6"
-			+ "f7255ba880dc7d8021e22ad6a85f0755");
-
-		private static readonly byte[] input_1024_5 = Hex.Decode(
-			  "8da89fd9e5f974a29feffb462b49180f"
-			+ "6cf9e802");
-
-		private static readonly byte[] seed_1024_5 = Hex.Decode(
-			  "b318c42df3be0f83fea823f5a7b47ed5"
-			+ "e425a3b5");
-
-		private static readonly byte[] output_1024_5 = Hex.Decode(
-			  "36f6e34d94a8d34daacba33a2139d00a"
-			+ "d85a9345a86051e73071620056b920e2"
-			+ "19005855a213a0f23897cdcd731b4525"
-			+ "7c777fe908202befdd0b58386b1244ea"
-			+ "0cf539a05d5d10329da44e13030fd760"
-			+ "dcd644cfef2094d1910d3f433e1c7c6d"
-			+ "d18bc1f2df7f643d662fb9dd37ead905"
-			+ "9190f4fa66ca39e869c4eb449cbdc439");
-
-		private static readonly byte[] input_1024_6 = Hex.Decode("26521050844271");
-
-		private static readonly byte[] seed_1024_6 = Hex.Decode(
-			  "e4ec0982c2336f3a677f6a356174eb0c"
-			+ "e887abc2");
-
-		private static readonly byte[] output_1024_6 = Hex.Decode(
-			  "42cee2617b1ecea4db3f4829386fbd61"
-			+ "dafbf038e180d837c96366df24c097b4"
-			+ "ab0fac6bdf590d821c9f10642e681ad0"
-			+ "5b8d78b378c0f46ce2fad63f74e0ad3d"
-			+ "f06b075d7eb5f5636f8d403b9059ca76"
-			+ "1b5c62bb52aa45002ea70baace08ded2"
-			+ "43b9d8cbd62a68ade265832b56564e43"
-			+ "a6fa42ed199a099769742df1539e8255");
-
-		private static readonly byte[] modulus_1027 = Hex.Decode(
-			  "051240b6cc0004fa48d0134671c078c7"
-			+ "c8dec3b3e2f25bc2564467339db38853"
-			+ "d06b85eea5b2de353bff42ac2e46bc97"
-			+ "fae6ac9618da9537a5c8f553c1e35762"
-			+ "5991d6108dcd7885fb3a25413f53efca"
-			+ "d948cb35cd9b9ae9c1c67626d113d57d"
-			+ "de4c5bea76bb5bb7de96c00d07372e96"
-			+ "85a6d75cf9d239fa148d70931b5f3fb0"
-			+ "39");
-
-		private static readonly byte[] pubExp_1027 = Hex.Decode("010001");
-
-		private static readonly byte[] privExp_1027 = Hex.Decode(
-			  "0411ffca3b7ca5e9e9be7fe38a85105e"
-			+ "353896db05c5796aecd2a725161eb365"
-			+ "1c8629a9b862b904d7b0c7b37f8cb5a1"
-			+ "c2b54001018a00a1eb2cafe4ee4e9492"
-			+ "c348bc2bedab4b9ebbf064e8eff322b9"
-			+ "009f8eec653905f40df88a3cdc49d456"
-			+ "7f75627d41aca624129b46a0b7c698e5"
-			+ "e65f2b7ba102c749a10135b6540d0401");
-
-		private static readonly byte[] prime1_1027 = Hex.Decode(
-			  "027458c19ec1636919e736c9af25d609"
-			+ "a51b8f561d19c6bf6943dd1ee1ab8a4a"
-			+ "3f232100bd40b88decc6ba235548b6ef"
-			+ "792a11c9de823d0a7922c7095b6eba57"
-			+ "01");
-
-		private static readonly byte[] prime2_1027 = Hex.Decode(
-			  "0210ee9b33ab61716e27d251bd465f4b"
-			+ "35a1a232e2da00901c294bf22350ce49"
-			+ "0d099f642b5375612db63ba1f2038649"
-			+ "2bf04d34b3c22bceb909d13441b53b51"
-			+ "39");
-
-		private static readonly byte[] primeExp1_1027 = Hex.Decode(
-			  "39fa028b826e88c1121b750a8b242fa9"
-			+ "a35c5b66bdfd1fa637d3cc48a84a4f45"
-			+ "7a194e7727e49f7bcc6e5a5a412657fc"
-			+ "470c7322ebc37416ef458c307a8c0901");
-
-		private static readonly byte[] primeExp2_1027 = Hex.Decode(
-			  "015d99a84195943979fa9e1be2c3c1b6"
-			+ "9f432f46fd03e47d5befbbbfd6b1d137"
-			+ "1d83efb330a3e020942b2fed115e5d02"
-			+ "be24fd92c9019d1cecd6dd4cf1e54cc8"
-			+ "99");
-
-		private static readonly byte[] crtCoef_1027 = Hex.Decode(
-			  "01f0b7015170b3f5e42223ba30301c41"
-			+ "a6d87cbb70e30cb7d3c67d25473db1f6"
-			+ "cbf03e3f9126e3e97968279a865b2c2b"
-			+ "426524cfc52a683d31ed30eb984be412"
-			+ "ba");
-
-		private static readonly byte[] input_1027_1 = Hex.Decode(
-			  "4a86609534ee434a6cbca3f7e962e76d"
-			+ "455e3264c19f605f6e5ff6137c65c56d"
-			+ "7fb344cd52bc93374f3d166c9f0c6f9c"
-			+ "506bad19330972d2");
-
-		private static readonly byte[] seed_1027_1 = Hex.Decode(
-			  "1cac19ce993def55f98203f6852896c9"
-			+ "5ccca1f3");
-
-		private static readonly byte[] output_1027_1 = Hex.Decode(
-			  "04cce19614845e094152a3fe18e54e33"
-			+ "30c44e5efbc64ae16886cb1869014cc5"
-			+ "781b1f8f9e045384d0112a135ca0d12e"
-			+ "9c88a8e4063416deaae3844f60d6e96f"
-			+ "e155145f4525b9a34431ca3766180f70"
-			+ "e15a5e5d8e8b1a516ff870609f13f896"
-			+ "935ced188279a58ed13d07114277d75c"
-			+ "6568607e0ab092fd803a223e4a8ee0b1"
-			+ "a8");
-
-		private static readonly byte[] input_1027_2 = Hex.Decode(
-			  "b0adc4f3fe11da59ce992773d9059943"
-			+ "c03046497ee9d9f9a06df1166db46d98"
-			+ "f58d27ec074c02eee6cbe2449c8b9fc5"
-			+ "080c5c3f4433092512ec46aa793743c8");
-
-		private static readonly byte[] seed_1027_2 = Hex.Decode(
-			  "f545d5897585e3db71aa0cb8da76c51d"
-			+ "032ae963");
-
-		private static readonly byte[] output_1027_2 = Hex.Decode(
-			  "0097b698c6165645b303486fbf5a2a44"
-			+ "79c0ee85889b541a6f0b858d6b6597b1"
-			+ "3b854eb4f839af03399a80d79bda6578"
-			+ "c841f90d645715b280d37143992dd186"
-			+ "c80b949b775cae97370e4ec97443136c"
-			+ "6da484e970ffdb1323a20847821d3b18"
-			+ "381de13bb49aaea66530c4a4b8271f3e"
-			+ "ae172cd366e07e6636f1019d2a28aed1"
-			+ "5e");
-
-		private static readonly byte[] input_1027_3 = Hex.Decode(
-			  "bf6d42e701707b1d0206b0c8b45a1c72"
-			+ "641ff12889219a82bdea965b5e79a96b"
-			+ "0d0163ed9d578ec9ada20f2fbcf1ea3c"
-			+ "4089d83419ba81b0c60f3606da99");
-
-		private static readonly byte[] seed_1027_3 = Hex.Decode(
-			  "ad997feef730d6ea7be60d0dc52e72ea"
-			+ "cbfdd275");
-
-		private static readonly byte[] output_1027_3 = Hex.Decode(
-			  "0301f935e9c47abcb48acbbe09895d9f"
-			+ "5971af14839da4ff95417ee453d1fd77"
-			+ "319072bb7297e1b55d7561cd9d1bb24c"
-			+ "1a9a37c619864308242804879d86ebd0"
-			+ "01dce5183975e1506989b70e5a834341"
-			+ "54d5cbfd6a24787e60eb0c658d2ac193"
-			+ "302d1192c6e622d4a12ad4b53923bca2"
-			+ "46df31c6395e37702c6a78ae081fb9d0"
-			+ "65");
-
-		private static readonly byte[] input_1027_4 = Hex.Decode(
-			  "fb2ef112f5e766eb94019297934794f7"
-			+ "be2f6fc1c58e");
-
-		private static readonly byte[] seed_1027_4 = Hex.Decode(
-			  "136454df5730f73c807a7e40d8c1a312"
-			+ "ac5b9dd3");
-
-		private static readonly byte[] output_1027_4 = Hex.Decode(
-			  "02d110ad30afb727beb691dd0cf17d0a"
-			+ "f1a1e7fa0cc040ec1a4ba26a42c59d0a"
-			+ "796a2e22c8f357ccc98b6519aceb682e"
-			+ "945e62cb734614a529407cd452bee3e4"
-			+ "4fece8423cc19e55548b8b994b849c7e"
-			+ "cde4933e76037e1d0ce44275b08710c6"
-			+ "8e430130b929730ed77e09b015642c55"
-			+ "93f04e4ffb9410798102a8e96ffdfe11"
-			+ "e4");
-
-		private static readonly byte[] input_1027_5 = Hex.Decode(
-			  "28ccd447bb9e85166dabb9e5b7d1adad"
-			+ "c4b9d39f204e96d5e440ce9ad928bc1c"
-			+ "2284");
-
-		private static readonly byte[] seed_1027_5 = Hex.Decode(
-			  "bca8057f824b2ea257f2861407eef63d"
-			+ "33208681");
-
-		private static readonly byte[] output_1027_5 = Hex.Decode(
-			  "00dbb8a7439d90efd919a377c54fae8f"
-			+ "e11ec58c3b858362e23ad1b8a4431079"
-			+ "9066b99347aa525691d2adc58d9b06e3"
-			+ "4f288c170390c5f0e11c0aa3645959f1"
-			+ "8ee79e8f2be8d7ac5c23d061f18dd74b"
-			+ "8c5f2a58fcb5eb0c54f99f01a8324756"
-			+ "8292536583340948d7a8c97c4acd1e98"
-			+ "d1e29dc320e97a260532a8aa7a758a1e"
-			+ "c2");
-
-		private static readonly byte[] input_1027_6 = Hex.Decode("f22242751ec6b1");
-
-		private static readonly byte[] seed_1027_6 = Hex.Decode(
-			  "2e7e1e17f647b5ddd033e15472f90f68"
-			+ "12f3ac4e");
-
-		private static readonly byte[] output_1027_6 = Hex.Decode(
-			  "00a5ffa4768c8bbecaee2db77e8f2eec"
-			+ "99595933545520835e5ba7db9493d3e1"
-			+ "7cddefe6a5f567624471908db4e2d83a"
-			+ "0fbee60608fc84049503b2234a07dc83"
-			+ "b27b22847ad8920ff42f674ef79b7628"
-			+ "0b00233d2b51b8cb2703a9d42bfbc825"
-			+ "0c96ec32c051e57f1b4ba528db89c37e"
-			+ "4c54e27e6e64ac69635ae887d9541619"
-			+ "a9");
-
-		private void OaepVecTest(
-			int					keySize,
-			int					no,
-			RsaKeyParameters	pubParam,
-			RsaKeyParameters	privParam,
-			byte[]				seed,
-			byte[]				input,
-			byte[]				output)
-		{
-			EncDec(keySize + " " + no, pubParam, privParam, seed, input, output);
-		}
-
-		public override string Name
-		{
-			get { return "OAEP"; }
-		}
-
-		public override void PerformTest()
-		{
-			BaseOaepTest(1, pubKeyEnc1, privKeyEnc1, output1);
-			BaseOaepTest(2, pubKeyEnc2, privKeyEnc2, output2);
-			BaseOaepTest(3, pubKeyEnc3, privKeyEnc3, output3);
-
-			RsaKeyParameters pubParam = new RsaKeyParameters(
-				false,
-				new BigInteger(1, modulus_1024),
-				new BigInteger(1, pubExp_1024));
-			RsaKeyParameters privParam = new RsaPrivateCrtKeyParameters(
-				pubParam.Modulus,
-				pubParam.Exponent,
-				new BigInteger(1, privExp_1024),
-				new BigInteger(1, prime1_1024),
-				new BigInteger(1, prime2_1024),
-				new BigInteger(1, primeExp1_1024),
-				new BigInteger(1, primeExp2_1024),
-				new BigInteger(1, crtCoef_1024));
-
-			OaepVecTest(1024, 1, pubParam, privParam, seed_1024_1, input_1024_1, output_1024_1);
-			OaepVecTest(1024, 2, pubParam, privParam, seed_1024_2, input_1024_2, output_1024_2);
-			OaepVecTest(1024, 3, pubParam, privParam, seed_1024_3, input_1024_3, output_1024_3);
-			OaepVecTest(1024, 4, pubParam, privParam, seed_1024_4, input_1024_4, output_1024_4);
-			OaepVecTest(1024, 5, pubParam, privParam, seed_1024_5, input_1024_5, output_1024_5);
-			OaepVecTest(1024, 6, pubParam, privParam, seed_1024_6, input_1024_6, output_1024_6);
-
-			pubParam = new RsaKeyParameters(
-				false,
-				new BigInteger(1, modulus_1027),
-				new BigInteger(1, pubExp_1027));
-			privParam = new RsaPrivateCrtKeyParameters(
-				pubParam.Modulus,
-				pubParam.Exponent,
-				new BigInteger(1, privExp_1027),
-				new BigInteger(1, prime1_1027),
-				new BigInteger(1, prime2_1027),
-				new BigInteger(1, primeExp1_1027),
-				new BigInteger(1, primeExp2_1027),
-				new BigInteger(1, crtCoef_1027));
-
-			OaepVecTest(1027, 1, pubParam, privParam, seed_1027_1, input_1027_1, output_1027_1);
-			OaepVecTest(1027, 2, pubParam, privParam, seed_1027_2, input_1027_2, output_1027_2);
-			OaepVecTest(1027, 3, pubParam, privParam, seed_1027_3, input_1027_3, output_1027_3);
-			OaepVecTest(1027, 4, pubParam, privParam, seed_1027_4, input_1027_4, output_1027_4);
-			OaepVecTest(1027, 5, pubParam, privParam, seed_1027_5, input_1027_5, output_1027_5);
-			OaepVecTest(1027, 6, pubParam, privParam, seed_1027_6, input_1027_6, output_1027_6);
-		}
-
-		public static void Main(
-			string[] args)
-		{
-			RunTest(new OaepTest());
-		}
-
-		[Test]
-		public void TestFunction()
-		{
-			string resultText = Perform().ToString();
-
-			Assert.AreEqual(Name + ": Okay", resultText);
-		}
-	}
+    [TestFixture]
+    public class OaepTest
+        : SimpleTest
+    {
+        private static readonly byte[] pubKeyEnc1 =
+        {
+            (byte)0x30, (byte)0x5a, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
+            (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
+            (byte)0x00, (byte)0x03, (byte)0x49, (byte)0x00, (byte)0x30, (byte)0x46, (byte)0x02, (byte)0x41,
+            (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce, (byte)0x88, (byte)0xac, (byte)0xfd,
+            (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f, (byte)0xc4, (byte)0x52, (byte)0x3f,
+            (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3, (byte)0x77, (byte)0x4a, (byte)0x25,
+            (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5, (byte)0xd9, (byte)0x9c, (byte)0xb5,
+            (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28, (byte)0x5e, (byte)0x53, (byte)0x01,
+            (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb, (byte)0x68, (byte)0x76, (byte)0x93,
+            (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62, (byte)0x4a, (byte)0x11, (byte)0xe0,
+            (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc, (byte)0xac, (byte)0xa0, (byte)0xa1,
+            (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11
+        };
+
+        private static readonly byte[] privKeyEnc1 =
+        {
+            (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x52, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+            (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+            (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
+            (byte)0x01, (byte)0x3c, (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x38, (byte)0x02, (byte)0x01,
+            (byte)0x00, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce,
+            (byte)0x88, (byte)0xac, (byte)0xfd, (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f,
+            (byte)0xc4, (byte)0x52, (byte)0x3f, (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3,
+            (byte)0x77, (byte)0x4a, (byte)0x25, (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5,
+            (byte)0xd9, (byte)0x9c, (byte)0xb5, (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28,
+            (byte)0x5e, (byte)0x53, (byte)0x01, (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb,
+            (byte)0x68, (byte)0x76, (byte)0x93, (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62,
+            (byte)0x4a, (byte)0x11, (byte)0xe0, (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc,
+            (byte)0xac, (byte)0xa0, (byte)0xa1, (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11, (byte)0x02,
+            (byte)0x40, (byte)0x0a, (byte)0x03, (byte)0x37, (byte)0x48, (byte)0x62, (byte)0x64, (byte)0x87,
+            (byte)0x69, (byte)0x5f, (byte)0x5f, (byte)0x30, (byte)0xbc, (byte)0x38, (byte)0xb9, (byte)0x8b,
+            (byte)0x44, (byte)0xc2, (byte)0xcd, (byte)0x2d, (byte)0xff, (byte)0x43, (byte)0x40, (byte)0x98,
+            (byte)0xcd, (byte)0x20, (byte)0xd8, (byte)0xa1, (byte)0x38, (byte)0xd0, (byte)0x90, (byte)0xbf,
+            (byte)0x64, (byte)0x79, (byte)0x7c, (byte)0x3f, (byte)0xa7, (byte)0xa2, (byte)0xcd, (byte)0xcb,
+            (byte)0x3c, (byte)0xd1, (byte)0xe0, (byte)0xbd, (byte)0xba, (byte)0x26, (byte)0x54, (byte)0xb4,
+            (byte)0xf9, (byte)0xdf, (byte)0x8e, (byte)0x8a, (byte)0xe5, (byte)0x9d, (byte)0x73, (byte)0x3d,
+            (byte)0x9f, (byte)0x33, (byte)0xb3, (byte)0x01, (byte)0x62, (byte)0x4a, (byte)0xfd, (byte)0x1d,
+            (byte)0x51, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xd8, (byte)0x40, (byte)0xb4, (byte)0x16,
+            (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4,
+            (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52,
+            (byte)0x4d, (byte)0x04, (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00,
+            (byte)0xaf, (byte)0x46, (byte)0x12, (byte)0x0d, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xc9,
+            (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4, (byte)0x53, (byte)0xf6, (byte)0x34,
+            (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1, (byte)0xd9, (byte)0x35, (byte)0x3f,
+            (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66, (byte)0xb1, (byte)0xd0, (byte)0x5a,
+            (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x89, (byte)0x02,
+            (byte)0x20, (byte)0x59, (byte)0x0b, (byte)0x95, (byte)0x72, (byte)0xa2, (byte)0xc2, (byte)0xa9,
+            (byte)0xc4, (byte)0x06, (byte)0x05, (byte)0x9d, (byte)0xc2, (byte)0xab, (byte)0x2f, (byte)0x1d,
+            (byte)0xaf, (byte)0xeb, (byte)0x7e, (byte)0x8b, (byte)0x4f, (byte)0x10, (byte)0xa7, (byte)0x54,
+            (byte)0x9e, (byte)0x8e, (byte)0xed, (byte)0xf5, (byte)0xb4, (byte)0xfc, (byte)0xe0, (byte)0x9e,
+            (byte)0x05, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0x8e, (byte)0x3c, (byte)0x05, (byte)0x21,
+            (byte)0xfe, (byte)0x15, (byte)0xe0, (byte)0xea, (byte)0x06, (byte)0xa3, (byte)0x6f, (byte)0xf0,
+            (byte)0xf1, (byte)0x0c, (byte)0x99, (byte)0x52, (byte)0xc3, (byte)0x5b, (byte)0x7a, (byte)0x75,
+            (byte)0x14, (byte)0xfd, (byte)0x32, (byte)0x38, (byte)0xb8, (byte)0x0a, (byte)0xad, (byte)0x52,
+            (byte)0x98, (byte)0x62, (byte)0x8d, (byte)0x51, (byte)0x02, (byte)0x20, (byte)0x36, (byte)0x3f,
+            (byte)0xf7, (byte)0x18, (byte)0x9d, (byte)0xa8, (byte)0xe9, (byte)0x0b, (byte)0x1d, (byte)0x34,
+            (byte)0x1f, (byte)0x71, (byte)0xd0, (byte)0x9b, (byte)0x76, (byte)0xa8, (byte)0xa9, (byte)0x43,
+            (byte)0xe1, (byte)0x1d, (byte)0x10, (byte)0xb2, (byte)0x4d, (byte)0x24, (byte)0x9f, (byte)0x2d,
+            (byte)0xea, (byte)0xfe, (byte)0xf8, (byte)0x0c, (byte)0x18, (byte)0x26
+        };
+
+        private static readonly byte[] output1 = 
+        { 
+            (byte)0x1b, (byte)0x8f, (byte)0x05, (byte)0xf9, (byte)0xca, (byte)0x1a, (byte)0x79, (byte)0x52,
+            (byte)0x6e, (byte)0x53, (byte)0xf3, (byte)0xcc, (byte)0x51, (byte)0x4f, (byte)0xdb, (byte)0x89,
+            (byte)0x2b, (byte)0xfb, (byte)0x91, (byte)0x93, (byte)0x23, (byte)0x1e, (byte)0x78, (byte)0xb9,
+            (byte)0x92, (byte)0xe6, (byte)0x8d, (byte)0x50, (byte)0xa4, (byte)0x80, (byte)0xcb, (byte)0x52,
+            (byte)0x33, (byte)0x89, (byte)0x5c, (byte)0x74, (byte)0x95, (byte)0x8d, (byte)0x5d, (byte)0x02,
+            (byte)0xab, (byte)0x8c, (byte)0x0f, (byte)0xd0, (byte)0x40, (byte)0xeb, (byte)0x58, (byte)0x44,
+            (byte)0xb0, (byte)0x05, (byte)0xc3, (byte)0x9e, (byte)0xd8, (byte)0x27, (byte)0x4a, (byte)0x9d,
+            (byte)0xbf, (byte)0xa8, (byte)0x06, (byte)0x71, (byte)0x40, (byte)0x94, (byte)0x39, (byte)0xd2
+        };
+
+        private static readonly byte[] pubKeyEnc2 =
+        {
+            (byte)0x30, (byte)0x4c, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
+            (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
+            (byte)0x00, (byte)0x03, (byte)0x3b, (byte)0x00, (byte)0x30, (byte)0x38, (byte)0x02, (byte)0x33,
+            (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d, (byte)0xfd,
+            (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78, (byte)0xb8,
+            (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c, (byte)0x98,
+            (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b, (byte)0x26,
+            (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b, (byte)0x69,
+            (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b, (byte)0xe8,
+            (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03
+        };
+
+        private static readonly byte[] privKeyEnc2 =
+        {
+            (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x13, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+            (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+            (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x81,
+            (byte)0xfe, (byte)0x30, (byte)0x81, (byte)0xfb, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02,
+            (byte)0x33, (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d,
+            (byte)0xfd, (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78,
+            (byte)0xb8, (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c,
+            (byte)0x98, (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b,
+            (byte)0x26, (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b,
+            (byte)0x69, (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b,
+            (byte)0xe8, (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03, (byte)0x02,
+            (byte)0x32, (byte)0x6c, (byte)0xaf, (byte)0xbc, (byte)0x60, (byte)0x94, (byte)0xb3, (byte)0xfe,
+            (byte)0x4c, (byte)0x72, (byte)0xb0, (byte)0xb3, (byte)0x32, (byte)0xc6, (byte)0xfb, (byte)0x25,
+            (byte)0xa2, (byte)0xb7, (byte)0x62, (byte)0x29, (byte)0x80, (byte)0x4e, (byte)0x68, (byte)0x65,
+            (byte)0xfc, (byte)0xa4, (byte)0x5a, (byte)0x74, (byte)0xdf, (byte)0x0f, (byte)0x8f, (byte)0xb8,
+            (byte)0x41, (byte)0x3b, (byte)0x52, (byte)0xc0, (byte)0xd0, (byte)0xe5, (byte)0x3d, (byte)0x9b,
+            (byte)0x59, (byte)0x0f, (byte)0xf1, (byte)0x9b, (byte)0xe7, (byte)0x9f, (byte)0x49, (byte)0xdd,
+            (byte)0x21, (byte)0xe5, (byte)0xeb, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0xcf, (byte)0x20,
+            (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4,
+            (byte)0x16, (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3,
+            (byte)0xb4, (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x91, (byte)0x02,
+            (byte)0x1a, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
+            (byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
+            (byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
+            (byte)0xb1, (byte)0xd0, (byte)0x5f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x8a, (byte)0x15,
+            (byte)0x78, (byte)0xac, (byte)0x5d, (byte)0x13, (byte)0xaf, (byte)0x10, (byte)0x2b, (byte)0x22,
+            (byte)0xb9, (byte)0x99, (byte)0xcd, (byte)0x74, (byte)0x61, (byte)0xf1, (byte)0x5e, (byte)0x6d,
+            (byte)0x22, (byte)0xcc, (byte)0x03, (byte)0x23, (byte)0xdf, (byte)0xdf, (byte)0x0b, (byte)0x02,
+            (byte)0x1a, (byte)0x00, (byte)0x86, (byte)0x55, (byte)0x21, (byte)0x4a, (byte)0xc5, (byte)0x4d,
+            (byte)0x8d, (byte)0x4e, (byte)0xcd, (byte)0x61, (byte)0x77, (byte)0xf1, (byte)0xc7, (byte)0x36,
+            (byte)0x90, (byte)0xce, (byte)0x2a, (byte)0x48, (byte)0x2c, (byte)0x8b, (byte)0x05, (byte)0x99,
+            (byte)0xcb, (byte)0xe0, (byte)0x3f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x83, (byte)0xef,
+            (byte)0xef, (byte)0xb8, (byte)0xa9, (byte)0xa4, (byte)0x0d, (byte)0x1d, (byte)0xb6, (byte)0xed,
+            (byte)0x98, (byte)0xad, (byte)0x84, (byte)0xed, (byte)0x13, (byte)0x35, (byte)0xdc, (byte)0xc1,
+            (byte)0x08, (byte)0xf3, (byte)0x22, (byte)0xd0, (byte)0x57, (byte)0xcf, (byte)0x8d
+        };
+
+        private static readonly byte[] output2 =
+        {
+            (byte)0x14, (byte)0xbd, (byte)0xdd, (byte)0x28, (byte)0xc9, (byte)0x83, (byte)0x35, (byte)0x19,
+            (byte)0x23, (byte)0x80, (byte)0xe8, (byte)0xe5, (byte)0x49, (byte)0xb1, (byte)0x58, (byte)0x2a,
+            (byte)0x8b, (byte)0x40, (byte)0xb4, (byte)0x48, (byte)0x6d, (byte)0x03, (byte)0xa6, (byte)0xa5,
+            (byte)0x31, (byte)0x1f, (byte)0x1f, (byte)0xd5, (byte)0xf0, (byte)0xa1, (byte)0x80, (byte)0xe4,
+            (byte)0x17, (byte)0x53, (byte)0x03, (byte)0x29, (byte)0xa9, (byte)0x34, (byte)0x90, (byte)0x74,
+            (byte)0xb1, (byte)0x52, (byte)0x13, (byte)0x54, (byte)0x29, (byte)0x08, (byte)0x24, (byte)0x52,
+            (byte)0x62, (byte)0x51
+        };
+
+        private static readonly byte[] pubKeyEnc3 =
+        {
+            (byte)0x30, (byte)0x81, (byte)0x9d, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a,
+            (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01,
+            (byte)0x05, (byte)0x00, (byte)0x03, (byte)0x81, (byte)0x8b, (byte)0x00, (byte)0x30, (byte)0x81,
+            (byte)0x87, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
+            (byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
+            (byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
+            (byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
+            (byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
+            (byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
+            (byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
+            (byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
+            (byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
+            (byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
+            (byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
+            (byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
+            (byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
+            (byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
+            (byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
+            (byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
+            (byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11
+        };
+
+        private static readonly byte[] privKeyEnc3 =
+        {
+            (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x75, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+            (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+            (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
+            (byte)0x02, (byte)0x5f, (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x5b, (byte)0x02, (byte)0x01,
+            (byte)0x00, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
+            (byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
+            (byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
+            (byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
+            (byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
+            (byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
+            (byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
+            (byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
+            (byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
+            (byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
+            (byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
+            (byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
+            (byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
+            (byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
+            (byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
+            (byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
+            (byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11,
+            (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xa5, (byte)0xda, (byte)0xfc, (byte)0x53,
+            (byte)0x41, (byte)0xfa, (byte)0xf2, (byte)0x89, (byte)0xc4, (byte)0xb9, (byte)0x88, (byte)0xdb,
+            (byte)0x30, (byte)0xc1, (byte)0xcd, (byte)0xf8, (byte)0x3f, (byte)0x31, (byte)0x25, (byte)0x1e,
+            (byte)0x06, (byte)0x68, (byte)0xb4, (byte)0x27, (byte)0x84, (byte)0x81, (byte)0x38, (byte)0x01,
+            (byte)0x57, (byte)0x96, (byte)0x41, (byte)0xb2, (byte)0x94, (byte)0x10, (byte)0xb3, (byte)0xc7,
+            (byte)0x99, (byte)0x8d, (byte)0x6b, (byte)0xc4, (byte)0x65, (byte)0x74, (byte)0x5e, (byte)0x5c,
+            (byte)0x39, (byte)0x26, (byte)0x69, (byte)0xd6, (byte)0x87, (byte)0x0d, (byte)0xa2, (byte)0xc0,
+            (byte)0x82, (byte)0xa9, (byte)0x39, (byte)0xe3, (byte)0x7f, (byte)0xdc, (byte)0xb8, (byte)0x2e,
+            (byte)0xc9, (byte)0x3e, (byte)0xda, (byte)0xc9, (byte)0x7f, (byte)0xf3, (byte)0xad, (byte)0x59,
+            (byte)0x50, (byte)0xac, (byte)0xcf, (byte)0xbc, (byte)0x11, (byte)0x1c, (byte)0x76, (byte)0xf1,
+            (byte)0xa9, (byte)0x52, (byte)0x94, (byte)0x44, (byte)0xe5, (byte)0x6a, (byte)0xaf, (byte)0x68,
+            (byte)0xc5, (byte)0x6c, (byte)0x09, (byte)0x2c, (byte)0xd3, (byte)0x8d, (byte)0xc3, (byte)0xbe,
+            (byte)0xf5, (byte)0xd2, (byte)0x0a, (byte)0x93, (byte)0x99, (byte)0x26, (byte)0xed, (byte)0x4f,
+            (byte)0x74, (byte)0xa1, (byte)0x3e, (byte)0xdd, (byte)0xfb, (byte)0xe1, (byte)0xa1, (byte)0xce,
+            (byte)0xcc, (byte)0x48, (byte)0x94, (byte)0xaf, (byte)0x94, (byte)0x28, (byte)0xc2, (byte)0xb7,
+            (byte)0xb8, (byte)0x88, (byte)0x3f, (byte)0xe4, (byte)0x46, (byte)0x3a, (byte)0x4b, (byte)0xc8,
+            (byte)0x5b, (byte)0x1c, (byte)0xb3, (byte)0xc1, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xee,
+            (byte)0xcf, (byte)0xae, (byte)0x81, (byte)0xb1, (byte)0xb9, (byte)0xb3, (byte)0xc9, (byte)0x08,
+            (byte)0x81, (byte)0x0b, (byte)0x10, (byte)0xa1, (byte)0xb5, (byte)0x60, (byte)0x01, (byte)0x99,
+            (byte)0xeb, (byte)0x9f, (byte)0x44, (byte)0xae, (byte)0xf4, (byte)0xfd, (byte)0xa4, (byte)0x93,
+            (byte)0xb8, (byte)0x1a, (byte)0x9e, (byte)0x3d, (byte)0x84, (byte)0xf6, (byte)0x32, (byte)0x12,
+            (byte)0x4e, (byte)0xf0, (byte)0x23, (byte)0x6e, (byte)0x5d, (byte)0x1e, (byte)0x3b, (byte)0x7e,
+            (byte)0x28, (byte)0xfa, (byte)0xe7, (byte)0xaa, (byte)0x04, (byte)0x0a, (byte)0x2d, (byte)0x5b,
+            (byte)0x25, (byte)0x21, (byte)0x76, (byte)0x45, (byte)0x9d, (byte)0x1f, (byte)0x39, (byte)0x75,
+            (byte)0x41, (byte)0xba, (byte)0x2a, (byte)0x58, (byte)0xfb, (byte)0x65, (byte)0x99, (byte)0x02,
+            (byte)0x41, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
+            (byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
+            (byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
+            (byte)0xb1, (byte)0xd0, (byte)0x5a, (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b,
+            (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4, (byte)0x16, (byte)0x66, (byte)0xb4,
+            (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4, (byte)0x32, (byte)0x04,
+            (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52, (byte)0x4d, (byte)0x04,
+            (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00, (byte)0xaf, (byte)0x46,
+            (byte)0x15, (byte)0x03, (byte)0x02, (byte)0x40, (byte)0x54, (byte)0x49, (byte)0x4c, (byte)0xa6,
+            (byte)0x3e, (byte)0xba, (byte)0x03, (byte)0x37, (byte)0xe4, (byte)0xe2, (byte)0x40, (byte)0x23,
+            (byte)0xfc, (byte)0xd6, (byte)0x9a, (byte)0x5a, (byte)0xeb, (byte)0x07, (byte)0xdd, (byte)0xdc,
+            (byte)0x01, (byte)0x83, (byte)0xa4, (byte)0xd0, (byte)0xac, (byte)0x9b, (byte)0x54, (byte)0xb0,
+            (byte)0x51, (byte)0xf2, (byte)0xb1, (byte)0x3e, (byte)0xd9, (byte)0x49, (byte)0x09, (byte)0x75,
+            (byte)0xea, (byte)0xb7, (byte)0x74, (byte)0x14, (byte)0xff, (byte)0x59, (byte)0xc1, (byte)0xf7,
+            (byte)0x69, (byte)0x2e, (byte)0x9a, (byte)0x2e, (byte)0x20, (byte)0x2b, (byte)0x38, (byte)0xfc,
+            (byte)0x91, (byte)0x0a, (byte)0x47, (byte)0x41, (byte)0x74, (byte)0xad, (byte)0xc9, (byte)0x3c,
+            (byte)0x1f, (byte)0x67, (byte)0xc9, (byte)0x81, (byte)0x02, (byte)0x40, (byte)0x47, (byte)0x1e,
+            (byte)0x02, (byte)0x90, (byte)0xff, (byte)0x0a, (byte)0xf0, (byte)0x75, (byte)0x03, (byte)0x51,
+            (byte)0xb7, (byte)0xf8, (byte)0x78, (byte)0x86, (byte)0x4c, (byte)0xa9, (byte)0x61, (byte)0xad,
+            (byte)0xbd, (byte)0x3a, (byte)0x8a, (byte)0x7e, (byte)0x99, (byte)0x1c, (byte)0x5c, (byte)0x05,
+            (byte)0x56, (byte)0xa9, (byte)0x4c, (byte)0x31, (byte)0x46, (byte)0xa7, (byte)0xf9, (byte)0x80,
+            (byte)0x3f, (byte)0x8f, (byte)0x6f, (byte)0x8a, (byte)0xe3, (byte)0x42, (byte)0xe9, (byte)0x31,
+            (byte)0xfd, (byte)0x8a, (byte)0xe4, (byte)0x7a, (byte)0x22, (byte)0x0d, (byte)0x1b, (byte)0x99,
+            (byte)0xa4, (byte)0x95, (byte)0x84, (byte)0x98, (byte)0x07, (byte)0xfe, (byte)0x39, (byte)0xf9,
+            (byte)0x24, (byte)0x5a, (byte)0x98, (byte)0x36, (byte)0xda, (byte)0x3d, (byte)0x02, (byte)0x41,
+            (byte)0x00, (byte)0xb0, (byte)0x6c, (byte)0x4f, (byte)0xda, (byte)0xbb, (byte)0x63, (byte)0x01,
+            (byte)0x19, (byte)0x8d, (byte)0x26, (byte)0x5b, (byte)0xdb, (byte)0xae, (byte)0x94, (byte)0x23,
+            (byte)0xb3, (byte)0x80, (byte)0xf2, (byte)0x71, (byte)0xf7, (byte)0x34, (byte)0x53, (byte)0x88,
+            (byte)0x50, (byte)0x93, (byte)0x07, (byte)0x7f, (byte)0xcd, (byte)0x39, (byte)0xe2, (byte)0x11,
+            (byte)0x9f, (byte)0xc9, (byte)0x86, (byte)0x32, (byte)0x15, (byte)0x4f, (byte)0x58, (byte)0x83,
+            (byte)0xb1, (byte)0x67, (byte)0xa9, (byte)0x67, (byte)0xbf, (byte)0x40, (byte)0x2b, (byte)0x4e,
+            (byte)0x9e, (byte)0x2e, (byte)0x0f, (byte)0x96, (byte)0x56, (byte)0xe6, (byte)0x98, (byte)0xea,
+            (byte)0x36, (byte)0x66, (byte)0xed, (byte)0xfb, (byte)0x25, (byte)0x79, (byte)0x80, (byte)0x39,
+            (byte)0xf7
+        };
+
+        private static readonly byte[] output3 = Hex.Decode(
+              "b8246b56a6ed5881aeb585d9a25b2ad790c417e080681bf1ac2bc3deb69d8bce"
+            + "f0c4366fec400af052a72e9b0effb5b3f2f192dbeaca03c12740057113bf1f06"
+            + "69ac22e9f3a7852e3c15d913cab0b8863a95c99294ce8674214954610346f4d4"
+            + "74b26f7c48b42ee68e1f572a1fc4026ac456b4f59f7b621ea1b9d88f64202fb1");
+
+        private static readonly byte[] seed = {
+            (byte)0xaa, (byte)0xfd, (byte)0x12, (byte)0xf6, (byte)0x59,
+            (byte)0xca, (byte)0xe6, (byte)0x34, (byte)0x89, (byte)0xb4,
+            (byte)0x79, (byte)0xe5, (byte)0x07, (byte)0x6d, (byte)0xde,
+            (byte)0xc2, (byte)0xf0, (byte)0x6c, (byte)0xb5, (byte)0x8f
+        };
+
+        private class VecRand
+            : SecureRandom
+        {
+            private readonly byte[] seed;
+
+            internal VecRand(byte[] seed)
+            {
+                this.seed = seed;
+            }
+
+            public override void NextBytes(
+                byte[] bytes)
+            {
+                Array.Copy(seed, 0, bytes, 0, bytes.Length);
+            }
+        }
+
+        private void BaseOaepTest(
+            int		id,
+            byte[]	pubKeyEnc,
+            byte[]	privKeyEnc,
+            byte[]	output)
+        {
+            //
+            // extract the public key info.
+            //
+            Asn1Object pubKeyObj = Asn1Object.FromByteArray(pubKeyEnc);
+            RsaPublicKeyStructure pubStruct = RsaPublicKeyStructure.GetInstance(
+                SubjectPublicKeyInfo.GetInstance(pubKeyObj).GetPublicKey());
+
+            //
+            // extract the private key info.
+            //
+            Asn1Object privKeyObj = Asn1Object.FromByteArray(privKeyEnc);
+            RsaPrivateKeyStructure privStruct = RsaPrivateKeyStructure.GetInstance(
+                PrivateKeyInfo.GetInstance(privKeyObj).ParsePrivateKey());
+
+            RsaKeyParameters pubParameters = new RsaKeyParameters(
+                false,
+                pubStruct.Modulus,
+                pubStruct.PublicExponent);
+
+            RsaKeyParameters privParameters = new RsaPrivateCrtKeyParameters(
+                privStruct.Modulus,
+                privStruct.PublicExponent,
+                privStruct.PrivateExponent,
+                privStruct.Prime1,
+                privStruct.Prime2,
+                privStruct.Exponent1,
+                privStruct.Exponent2,
+                privStruct.Coefficient);
+
+            byte[] input = new byte[] {
+                (byte)0x54, (byte)0x85, (byte)0x9b, (byte)0x34,
+                (byte)0x2c, (byte)0x49, (byte)0xea, (byte)0x2a
+            };
+
+            EncDec("id(" + id + ")", pubParameters, privParameters, seed, input, output);
+        }
+
+        private void EncDec(
+            string				label,
+            RsaKeyParameters	pubParameters,
+            RsaKeyParameters	privParameters,
+            byte[]				seed,
+            byte[]				input,
+            byte[]				output)
+        {
+            IAsymmetricBlockCipher cipher = new OaepEncoding(new RsaEngine());
+
+            cipher.Init(true, new ParametersWithRandom(pubParameters, new VecRand(seed)));
+
+            byte[] outBytes = cipher.ProcessBlock(input, 0, input.Length);
+
+            for (int i = 0; i != output.Length; i++)
+            {
+                if (outBytes[i] != output[i])
+                {
+                    Fail(label + " failed encryption");
+                }
+            }
+
+            cipher.Init(false, privParameters);
+
+            outBytes = cipher.ProcessBlock(output, 0, output.Length);
+
+            for (int i = 0; i != input.Length; i++)
+            {
+                if (outBytes[i] != input[i])
+                {
+                    Fail(label + " failed decoding");
+                }
+            }
+        }
+
+        /*
+        * RSA vector tests from PKCS#1 page
+        */
+        private static readonly byte[] modulus_1024 = Hex.Decode(
+              "a8b3b284af8eb50b387034a860f146c4"
+            + "919f318763cd6c5598c8ae4811a1e0ab"
+            + "c4c7e0b082d693a5e7fced675cf46685"
+            + "12772c0cbc64a742c6c630f533c8cc72"
+            + "f62ae833c40bf25842e984bb78bdbf97"
+            + "c0107d55bdb662f5c4e0fab9845cb514"
+            + "8ef7392dd3aaff93ae1e6b667bb3d424"
+            + "7616d4f5ba10d4cfd226de88d39f16fb");
+
+        private static readonly byte[] pubExp_1024 = Hex.Decode("010001");
+
+        private static readonly byte[] privExp_1024 = Hex.Decode(
+              "53339cfdb79fc8466a655c7316aca85c"
+            + "55fd8f6dd898fdaf119517ef4f52e8fd"
+            + "8e258df93fee180fa0e4ab29693cd83b"
+            + "152a553d4ac4d1812b8b9fa5af0e7f55"
+            + "fe7304df41570926f3311f15c4d65a73"
+            + "2c483116ee3d3d2d0af3549ad9bf7cbf"
+            + "b78ad884f84d5beb04724dc7369b31de"
+            + "f37d0cf539e9cfcdd3de653729ead5d1");
+
+        private static readonly byte[] prime1_1024 = Hex.Decode(
+              "d32737e7267ffe1341b2d5c0d150a81b"
+            + "586fb3132bed2f8d5262864a9cb9f30a"
+            + "f38be448598d413a172efb802c21acf1"
+            + "c11c520c2f26a471dcad212eac7ca39d");
+
+        private static readonly byte[] prime2_1024 = Hex.Decode(
+              "cc8853d1d54da630fac004f471f281c7"
+            + "b8982d8224a490edbeb33d3e3d5cc93c"
+            + "4765703d1dd791642f1f116a0dd852be"
+            + "2419b2af72bfe9a030e860b0288b5d77");
+
+        private static readonly byte[] primeExp1_1024 = Hex.Decode(
+              "0e12bf1718e9cef5599ba1c3882fe804"
+            + "6a90874eefce8f2ccc20e4f2741fb0a3"
+            + "3a3848aec9c9305fbecbd2d76819967d"
+            + "4671acc6431e4037968db37878e695c1");
+
+        private static readonly byte[] primeExp2_1024 = Hex.Decode(
+              "95297b0f95a2fa67d00707d609dfd4fc"
+            + "05c89dafc2ef6d6ea55bec771ea33373"
+            + "4d9251e79082ecda866efef13c459e1a"
+            + "631386b7e354c899f5f112ca85d71583");
+
+        private static readonly byte[] crtCoef_1024 = Hex.Decode(
+              "4f456c502493bdc0ed2ab756a3a6ed4d"
+            + "67352a697d4216e93212b127a63d5411"
+            + "ce6fa98d5dbefd73263e372814274381"
+            + "8166ed7dd63687dd2a8ca1d2f4fbd8e1");
+
+        private static readonly byte[] input_1024_1 = Hex.Decode(
+              "6628194e12073db03ba94cda9ef95323"
+            + "97d50dba79b987004afefe34");
+
+        private static readonly byte[] seed_1024_1 = Hex.Decode(
+              "18b776ea21069d69776a33e96bad48e1"
+            + "dda0a5ef");
+
+        private static readonly byte[] output_1024_1 = Hex.Decode(
+              "354fe67b4a126d5d35fe36c777791a3f"
+            + "7ba13def484e2d3908aff722fad468fb"
+            + "21696de95d0be911c2d3174f8afcc201"
+            + "035f7b6d8e69402de5451618c21a535f"
+            + "a9d7bfc5b8dd9fc243f8cf927db31322"
+            + "d6e881eaa91a996170e657a05a266426"
+            + "d98c88003f8477c1227094a0d9fa1e8c"
+            + "4024309ce1ecccb5210035d47ac72e8a");
+
+        private static readonly byte[] input_1024_2 = Hex.Decode(
+              "750c4047f547e8e41411856523298ac9"
+            + "bae245efaf1397fbe56f9dd5");
+
+        private static readonly byte[] seed_1024_2 = Hex.Decode(
+              "0cc742ce4a9b7f32f951bcb251efd925"
+            + "fe4fe35f");
+
+        private static readonly byte[] output_1024_2 = Hex.Decode(
+              "640db1acc58e0568fe5407e5f9b701df"
+            + "f8c3c91e716c536fc7fcec6cb5b71c11"
+            + "65988d4a279e1577d730fc7a29932e3f"
+            + "00c81515236d8d8e31017a7a09df4352"
+            + "d904cdeb79aa583adcc31ea698a4c052"
+            + "83daba9089be5491f67c1a4ee48dc74b"
+            + "bbe6643aef846679b4cb395a352d5ed1"
+            + "15912df696ffe0702932946d71492b44");
+
+        private static readonly byte[] input_1024_3 = Hex.Decode(
+              "d94ae0832e6445ce42331cb06d531a82"
+            + "b1db4baad30f746dc916df24d4e3c245"
+            + "1fff59a6423eb0e1d02d4fe646cf699d"
+            + "fd818c6e97b051");
+
+        private static readonly byte[] seed_1024_3 = Hex.Decode(
+              "2514df4695755a67b288eaf4905c36ee"
+            + "c66fd2fd");
+
+        private static readonly byte[] output_1024_3 = Hex.Decode(
+              "423736ed035f6026af276c35c0b3741b"
+            + "365e5f76ca091b4e8c29e2f0befee603"
+            + "595aa8322d602d2e625e95eb81b2f1c9"
+            + "724e822eca76db8618cf09c5343503a4"
+            + "360835b5903bc637e3879fb05e0ef326"
+            + "85d5aec5067cd7cc96fe4b2670b6eac3"
+            + "066b1fcf5686b68589aafb7d629b02d8"
+            + "f8625ca3833624d4800fb081b1cf94eb");
+
+        private static readonly byte[] input_1024_4 = Hex.Decode(
+              "52e650d98e7f2a048b4f86852153b97e"
+            + "01dd316f346a19f67a85");
+
+        private static readonly byte[] seed_1024_4 = Hex.Decode(
+              "c4435a3e1a18a68b6820436290a37cef"
+            + "b85db3fb");
+
+        private static readonly byte[] output_1024_4 = Hex.Decode(
+              "45ead4ca551e662c9800f1aca8283b05"
+            + "25e6abae30be4b4aba762fa40fd3d38e"
+            + "22abefc69794f6ebbbc05ddbb1121624"
+            + "7d2f412fd0fba87c6e3acd888813646f"
+            + "d0e48e785204f9c3f73d6d8239562722"
+            + "dddd8771fec48b83a31ee6f592c4cfd4"
+            + "bc88174f3b13a112aae3b9f7b80e0fc6"
+            + "f7255ba880dc7d8021e22ad6a85f0755");
+
+        private static readonly byte[] input_1024_5 = Hex.Decode(
+              "8da89fd9e5f974a29feffb462b49180f"
+            + "6cf9e802");
+
+        private static readonly byte[] seed_1024_5 = Hex.Decode(
+              "b318c42df3be0f83fea823f5a7b47ed5"
+            + "e425a3b5");
+
+        private static readonly byte[] output_1024_5 = Hex.Decode(
+              "36f6e34d94a8d34daacba33a2139d00a"
+            + "d85a9345a86051e73071620056b920e2"
+            + "19005855a213a0f23897cdcd731b4525"
+            + "7c777fe908202befdd0b58386b1244ea"
+            + "0cf539a05d5d10329da44e13030fd760"
+            + "dcd644cfef2094d1910d3f433e1c7c6d"
+            + "d18bc1f2df7f643d662fb9dd37ead905"
+            + "9190f4fa66ca39e869c4eb449cbdc439");
+
+        private static readonly byte[] input_1024_6 = Hex.Decode("26521050844271");
+
+        private static readonly byte[] seed_1024_6 = Hex.Decode(
+              "e4ec0982c2336f3a677f6a356174eb0c"
+            + "e887abc2");
+
+        private static readonly byte[] output_1024_6 = Hex.Decode(
+              "42cee2617b1ecea4db3f4829386fbd61"
+            + "dafbf038e180d837c96366df24c097b4"
+            + "ab0fac6bdf590d821c9f10642e681ad0"
+            + "5b8d78b378c0f46ce2fad63f74e0ad3d"
+            + "f06b075d7eb5f5636f8d403b9059ca76"
+            + "1b5c62bb52aa45002ea70baace08ded2"
+            + "43b9d8cbd62a68ade265832b56564e43"
+            + "a6fa42ed199a099769742df1539e8255");
+
+        private static readonly byte[] modulus_1027 = Hex.Decode(
+              "051240b6cc0004fa48d0134671c078c7"
+            + "c8dec3b3e2f25bc2564467339db38853"
+            + "d06b85eea5b2de353bff42ac2e46bc97"
+            + "fae6ac9618da9537a5c8f553c1e35762"
+            + "5991d6108dcd7885fb3a25413f53efca"
+            + "d948cb35cd9b9ae9c1c67626d113d57d"
+            + "de4c5bea76bb5bb7de96c00d07372e96"
+            + "85a6d75cf9d239fa148d70931b5f3fb0"
+            + "39");
+
+        private static readonly byte[] pubExp_1027 = Hex.Decode("010001");
+
+        private static readonly byte[] privExp_1027 = Hex.Decode(
+              "0411ffca3b7ca5e9e9be7fe38a85105e"
+            + "353896db05c5796aecd2a725161eb365"
+            + "1c8629a9b862b904d7b0c7b37f8cb5a1"
+            + "c2b54001018a00a1eb2cafe4ee4e9492"
+            + "c348bc2bedab4b9ebbf064e8eff322b9"
+            + "009f8eec653905f40df88a3cdc49d456"
+            + "7f75627d41aca624129b46a0b7c698e5"
+            + "e65f2b7ba102c749a10135b6540d0401");
+
+        private static readonly byte[] prime1_1027 = Hex.Decode(
+              "027458c19ec1636919e736c9af25d609"
+            + "a51b8f561d19c6bf6943dd1ee1ab8a4a"
+            + "3f232100bd40b88decc6ba235548b6ef"
+            + "792a11c9de823d0a7922c7095b6eba57"
+            + "01");
+
+        private static readonly byte[] prime2_1027 = Hex.Decode(
+              "0210ee9b33ab61716e27d251bd465f4b"
+            + "35a1a232e2da00901c294bf22350ce49"
+            + "0d099f642b5375612db63ba1f2038649"
+            + "2bf04d34b3c22bceb909d13441b53b51"
+            + "39");
+
+        private static readonly byte[] primeExp1_1027 = Hex.Decode(
+              "39fa028b826e88c1121b750a8b242fa9"
+            + "a35c5b66bdfd1fa637d3cc48a84a4f45"
+            + "7a194e7727e49f7bcc6e5a5a412657fc"
+            + "470c7322ebc37416ef458c307a8c0901");
+
+        private static readonly byte[] primeExp2_1027 = Hex.Decode(
+              "015d99a84195943979fa9e1be2c3c1b6"
+            + "9f432f46fd03e47d5befbbbfd6b1d137"
+            + "1d83efb330a3e020942b2fed115e5d02"
+            + "be24fd92c9019d1cecd6dd4cf1e54cc8"
+            + "99");
+
+        private static readonly byte[] crtCoef_1027 = Hex.Decode(
+              "01f0b7015170b3f5e42223ba30301c41"
+            + "a6d87cbb70e30cb7d3c67d25473db1f6"
+            + "cbf03e3f9126e3e97968279a865b2c2b"
+            + "426524cfc52a683d31ed30eb984be412"
+            + "ba");
+
+        private static readonly byte[] input_1027_1 = Hex.Decode(
+              "4a86609534ee434a6cbca3f7e962e76d"
+            + "455e3264c19f605f6e5ff6137c65c56d"
+            + "7fb344cd52bc93374f3d166c9f0c6f9c"
+            + "506bad19330972d2");
+
+        private static readonly byte[] seed_1027_1 = Hex.Decode(
+              "1cac19ce993def55f98203f6852896c9"
+            + "5ccca1f3");
+
+        private static readonly byte[] output_1027_1 = Hex.Decode(
+              "04cce19614845e094152a3fe18e54e33"
+            + "30c44e5efbc64ae16886cb1869014cc5"
+            + "781b1f8f9e045384d0112a135ca0d12e"
+            + "9c88a8e4063416deaae3844f60d6e96f"
+            + "e155145f4525b9a34431ca3766180f70"
+            + "e15a5e5d8e8b1a516ff870609f13f896"
+            + "935ced188279a58ed13d07114277d75c"
+            + "6568607e0ab092fd803a223e4a8ee0b1"
+            + "a8");
+
+        private static readonly byte[] input_1027_2 = Hex.Decode(
+              "b0adc4f3fe11da59ce992773d9059943"
+            + "c03046497ee9d9f9a06df1166db46d98"
+            + "f58d27ec074c02eee6cbe2449c8b9fc5"
+            + "080c5c3f4433092512ec46aa793743c8");
+
+        private static readonly byte[] seed_1027_2 = Hex.Decode(
+              "f545d5897585e3db71aa0cb8da76c51d"
+            + "032ae963");
+
+        private static readonly byte[] output_1027_2 = Hex.Decode(
+              "0097b698c6165645b303486fbf5a2a44"
+            + "79c0ee85889b541a6f0b858d6b6597b1"
+            + "3b854eb4f839af03399a80d79bda6578"
+            + "c841f90d645715b280d37143992dd186"
+            + "c80b949b775cae97370e4ec97443136c"
+            + "6da484e970ffdb1323a20847821d3b18"
+            + "381de13bb49aaea66530c4a4b8271f3e"
+            + "ae172cd366e07e6636f1019d2a28aed1"
+            + "5e");
+
+        private static readonly byte[] input_1027_3 = Hex.Decode(
+              "bf6d42e701707b1d0206b0c8b45a1c72"
+            + "641ff12889219a82bdea965b5e79a96b"
+            + "0d0163ed9d578ec9ada20f2fbcf1ea3c"
+            + "4089d83419ba81b0c60f3606da99");
+
+        private static readonly byte[] seed_1027_3 = Hex.Decode(
+              "ad997feef730d6ea7be60d0dc52e72ea"
+            + "cbfdd275");
+
+        private static readonly byte[] output_1027_3 = Hex.Decode(
+              "0301f935e9c47abcb48acbbe09895d9f"
+            + "5971af14839da4ff95417ee453d1fd77"
+            + "319072bb7297e1b55d7561cd9d1bb24c"
+            + "1a9a37c619864308242804879d86ebd0"
+            + "01dce5183975e1506989b70e5a834341"
+            + "54d5cbfd6a24787e60eb0c658d2ac193"
+            + "302d1192c6e622d4a12ad4b53923bca2"
+            + "46df31c6395e37702c6a78ae081fb9d0"
+            + "65");
+
+        private static readonly byte[] input_1027_4 = Hex.Decode(
+              "fb2ef112f5e766eb94019297934794f7"
+            + "be2f6fc1c58e");
+
+        private static readonly byte[] seed_1027_4 = Hex.Decode(
+              "136454df5730f73c807a7e40d8c1a312"
+            + "ac5b9dd3");
+
+        private static readonly byte[] output_1027_4 = Hex.Decode(
+              "02d110ad30afb727beb691dd0cf17d0a"
+            + "f1a1e7fa0cc040ec1a4ba26a42c59d0a"
+            + "796a2e22c8f357ccc98b6519aceb682e"
+            + "945e62cb734614a529407cd452bee3e4"
+            + "4fece8423cc19e55548b8b994b849c7e"
+            + "cde4933e76037e1d0ce44275b08710c6"
+            + "8e430130b929730ed77e09b015642c55"
+            + "93f04e4ffb9410798102a8e96ffdfe11"
+            + "e4");
+
+        private static readonly byte[] input_1027_5 = Hex.Decode(
+              "28ccd447bb9e85166dabb9e5b7d1adad"
+            + "c4b9d39f204e96d5e440ce9ad928bc1c"
+            + "2284");
+
+        private static readonly byte[] seed_1027_5 = Hex.Decode(
+              "bca8057f824b2ea257f2861407eef63d"
+            + "33208681");
+
+        private static readonly byte[] output_1027_5 = Hex.Decode(
+              "00dbb8a7439d90efd919a377c54fae8f"
+            + "e11ec58c3b858362e23ad1b8a4431079"
+            + "9066b99347aa525691d2adc58d9b06e3"
+            + "4f288c170390c5f0e11c0aa3645959f1"
+            + "8ee79e8f2be8d7ac5c23d061f18dd74b"
+            + "8c5f2a58fcb5eb0c54f99f01a8324756"
+            + "8292536583340948d7a8c97c4acd1e98"
+            + "d1e29dc320e97a260532a8aa7a758a1e"
+            + "c2");
+
+        private static readonly byte[] input_1027_6 = Hex.Decode("f22242751ec6b1");
+
+        private static readonly byte[] seed_1027_6 = Hex.Decode(
+              "2e7e1e17f647b5ddd033e15472f90f68"
+            + "12f3ac4e");
+
+        private static readonly byte[] output_1027_6 = Hex.Decode(
+              "00a5ffa4768c8bbecaee2db77e8f2eec"
+            + "99595933545520835e5ba7db9493d3e1"
+            + "7cddefe6a5f567624471908db4e2d83a"
+            + "0fbee60608fc84049503b2234a07dc83"
+            + "b27b22847ad8920ff42f674ef79b7628"
+            + "0b00233d2b51b8cb2703a9d42bfbc825"
+            + "0c96ec32c051e57f1b4ba528db89c37e"
+            + "4c54e27e6e64ac69635ae887d9541619"
+            + "a9");
+
+        private void OaepVecTest(
+            int					keySize,
+            int					no,
+            RsaKeyParameters	pubParam,
+            RsaKeyParameters	privParam,
+            byte[]				seed,
+            byte[]				input,
+            byte[]				output)
+        {
+            EncDec(keySize + " " + no, pubParam, privParam, seed, input, output);
+        }
+
+        public override string Name
+        {
+            get { return "OAEP"; }
+        }
+
+        public override void PerformTest()
+        {
+            BaseOaepTest(1, pubKeyEnc1, privKeyEnc1, output1);
+            BaseOaepTest(2, pubKeyEnc2, privKeyEnc2, output2);
+            BaseOaepTest(3, pubKeyEnc3, privKeyEnc3, output3);
+
+            RsaKeyParameters pubParam = new RsaKeyParameters(
+                false,
+                new BigInteger(1, modulus_1024),
+                new BigInteger(1, pubExp_1024));
+            RsaKeyParameters privParam = new RsaPrivateCrtKeyParameters(
+                pubParam.Modulus,
+                pubParam.Exponent,
+                new BigInteger(1, privExp_1024),
+                new BigInteger(1, prime1_1024),
+                new BigInteger(1, prime2_1024),
+                new BigInteger(1, primeExp1_1024),
+                new BigInteger(1, primeExp2_1024),
+                new BigInteger(1, crtCoef_1024));
+
+            OaepVecTest(1024, 1, pubParam, privParam, seed_1024_1, input_1024_1, output_1024_1);
+            OaepVecTest(1024, 2, pubParam, privParam, seed_1024_2, input_1024_2, output_1024_2);
+            OaepVecTest(1024, 3, pubParam, privParam, seed_1024_3, input_1024_3, output_1024_3);
+            OaepVecTest(1024, 4, pubParam, privParam, seed_1024_4, input_1024_4, output_1024_4);
+            OaepVecTest(1024, 5, pubParam, privParam, seed_1024_5, input_1024_5, output_1024_5);
+            OaepVecTest(1024, 6, pubParam, privParam, seed_1024_6, input_1024_6, output_1024_6);
+
+            pubParam = new RsaKeyParameters(
+                false,
+                new BigInteger(1, modulus_1027),
+                new BigInteger(1, pubExp_1027));
+            privParam = new RsaPrivateCrtKeyParameters(
+                pubParam.Modulus,
+                pubParam.Exponent,
+                new BigInteger(1, privExp_1027),
+                new BigInteger(1, prime1_1027),
+                new BigInteger(1, prime2_1027),
+                new BigInteger(1, primeExp1_1027),
+                new BigInteger(1, primeExp2_1027),
+                new BigInteger(1, crtCoef_1027));
+
+            OaepVecTest(1027, 1, pubParam, privParam, seed_1027_1, input_1027_1, output_1027_1);
+            OaepVecTest(1027, 2, pubParam, privParam, seed_1027_2, input_1027_2, output_1027_2);
+            OaepVecTest(1027, 3, pubParam, privParam, seed_1027_3, input_1027_3, output_1027_3);
+            OaepVecTest(1027, 4, pubParam, privParam, seed_1027_4, input_1027_4, output_1027_4);
+            OaepVecTest(1027, 5, pubParam, privParam, seed_1027_5, input_1027_5, output_1027_5);
+            OaepVecTest(1027, 6, pubParam, privParam, seed_1027_6, input_1027_6, output_1027_6);
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new OaepTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
 }
diff --git a/crypto/test/src/crypto/test/OCBTest.cs b/crypto/test/src/crypto/test/OCBTest.cs
index 0829cb078..9f898fbe2 100644
--- a/crypto/test/src/crypto/test/OCBTest.cs
+++ b/crypto/test/src/crypto/test/OCBTest.cs
@@ -6,16 +6,17 @@ using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
 using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.Crypto.Tests
 {
     /**
-     * Test vectors from the "work in progress" Internet-Draft <a
-     * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-07">The OCB Authenticated-Encryption
-     * Algorithm</a>
+     * Test vectors from <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB
+     * Authenticated-Encryption Algorithm</a>
      */
     public class OcbTest
         : SimpleTest
@@ -120,16 +121,18 @@ namespace Org.BouncyCastle.Crypto.Tests
                 RunTestCase("Test Case " + i, TEST_VECTORS_96[i], 96, K96);
             }
 
-            RunLongerTestCase(128, 128, Hex.Decode("67E944D23256C5E0B6C61FA22FDF1EA2"));
-            RunLongerTestCase(192, 128, Hex.Decode("F673F2C3E7174AAE7BAE986CA9F29E17"));
-            RunLongerTestCase(256, 128, Hex.Decode("D90EB8E9C977C88B79DD793D7FFA161C"));
-            RunLongerTestCase(128, 96, Hex.Decode("77A3D8E73589158D25D01209"));
-            RunLongerTestCase(192, 96, Hex.Decode("05D56EAD2752C86BE6932C5E"));
-            RunLongerTestCase(256, 96, Hex.Decode("5458359AC23B0CBA9E6330DD"));
-            RunLongerTestCase(128, 64, Hex.Decode("192C9B7BD90BA06A"));
-            RunLongerTestCase(192, 64, Hex.Decode("0066BC6E0EF34E24"));
-            RunLongerTestCase(256, 64, Hex.Decode("7D4EA5D445501CBE"));
-
+            RunLongerTestCase(128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2");
+            RunLongerTestCase(192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17");
+            RunLongerTestCase(256, 128, "D90EB8E9C977C88B79DD793D7FFA161C");
+            RunLongerTestCase(128, 96, "77A3D8E73589158D25D01209");
+            RunLongerTestCase(192, 96, "05D56EAD2752C86BE6932C5E");
+            RunLongerTestCase(256, 96, "5458359AC23B0CBA9E6330DD");
+            RunLongerTestCase(128, 64, "192C9B7BD90BA06A");
+            RunLongerTestCase(192, 64, "0066BC6E0EF34E24");
+            RunLongerTestCase(256, 64, "7D4EA5D445501CBE");
+
+            RandomTests();
+            OutputSizeTests();
             DoTestExceptions();
         }
 
@@ -172,18 +175,20 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             int macLengthBytes = macLengthBits / 8;
 
-            // TODO Variations processing AAD and cipher bytes incrementally
-
             KeyParameter keyParameter = new KeyParameter(K);
-            AeadParameters aeadParameters = new AeadParameters(keyParameter, macLengthBits, N, A);
+            AeadParameters parameters = new AeadParameters(keyParameter, macLengthBits, N, A);
 
-            IAeadBlockCipher encCipher = InitOcbCipher(true, aeadParameters);
-            IAeadBlockCipher decCipher = InitOcbCipher(false, aeadParameters);
+            IAeadBlockCipher encCipher = InitOcbCipher(true, parameters);
+            IAeadBlockCipher decCipher = InitOcbCipher(false, parameters);
 
             CheckTestCase(encCipher, decCipher, testName, macLengthBytes, P, C);
             CheckTestCase(encCipher, decCipher, testName + " (reused)", macLengthBytes, P, C);
 
-            // TODO Key reuse
+            // Key reuse
+            AeadParameters keyReuseParams = AeadTestUtilities.ReuseKey(parameters);
+            encCipher.Init(true, keyReuseParams);
+            decCipher.Init(false, keyReuseParams);
+            CheckTestCase(encCipher, decCipher, testName + " (key reuse)", macLengthBytes, P, C);
         }
 
         private IBlockCipher CreateUnderlyingCipher()
@@ -206,7 +211,7 @@ namespace Org.BouncyCastle.Crypto.Tests
         private void CheckTestCase(IAeadBlockCipher encCipher, IAeadBlockCipher decCipher, string testName,
             int macLengthBytes, byte[] P, byte[] C)
         {
-            byte[] tag = Arrays.Copy(C, C.Length - macLengthBytes, macLengthBytes);
+            byte[] tag = Arrays.CopyOfRange(C, C.Length - macLengthBytes, C.Length);
 
             {
                 byte[] enc = new byte[encCipher.GetOutputSize(P.Length)];
@@ -251,14 +256,14 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
         }
 
-        private void RunLongerTestCase(int keyLen, int tagLen, byte[] expectedOutput)
+        private void RunLongerTestCase(int keyLen, int tagLen, string expectedOutputHex)
         {
+            byte[] expectedOutput = Hex.Decode(expectedOutputHex);
             byte[] keyBytes = new byte[keyLen / 8];
             keyBytes[keyBytes.Length - 1] = (byte)tagLen;
             KeyParameter key = new KeyParameter(keyBytes);
 
             IAeadBlockCipher c1 = InitOcbCipher(true, new AeadParameters(key, tagLen, CreateNonce(385)));
-
             IAeadBlockCipher c2 = CreateOcbCipher();
 
             long total = 0;
@@ -322,6 +327,177 @@ namespace Org.BouncyCastle.Crypto.Tests
             return len;
         }
 
+        private void RandomTests()
+        {
+            SecureRandom srng = new SecureRandom();
+            srng.SetSeed(DateTimeUtilities.CurrentUnixMs());
+            for (int i = 0; i < 10; ++i)
+            {
+                RandomTest(srng);
+            }
+        }
+
+        private void RandomTest(SecureRandom srng)
+        {
+            int kLength = 16 + 8 * (System.Math.Abs(srng.NextInt()) % 3);
+            byte[] K = new byte[kLength];
+            srng.NextBytes(K);
+
+            int pLength = (int)((uint)srng.NextInt() >> 16);
+            byte[] P = new byte[pLength];
+            srng.NextBytes(P);
+
+            int aLength = (int)((uint)srng.NextInt() >> 24);
+            byte[] A = new byte[aLength];
+            srng.NextBytes(A);
+
+            int saLength = (int)((uint)srng.NextInt() >> 24);
+            byte[] SA = new byte[saLength];
+            srng.NextBytes(SA);
+
+            int ivLength = 1 + NextInt(srng, 15);
+            byte[] IV = new byte[ivLength];
+            srng.NextBytes(IV);
+
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, IV, A);
+            IAeadBlockCipher cipher = InitOcbCipher(true, parameters);
+            byte[] C = new byte[cipher.GetOutputSize(P.Length)];
+            int predicted = cipher.GetUpdateOutputSize(P.Length);
+
+            int split = NextInt(srng, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            int len = cipher.ProcessBytes(P, 0, P.Length, C, 0);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            if (predicted != len)
+            {
+                Fail("encryption reported incorrect update length in randomised test");
+            }
+
+            len += cipher.DoFinal(C, len);
+
+            if (C.Length != len)
+            {
+                Fail("encryption reported incorrect length in randomised test");
+            }
+
+            byte[] encT = cipher.GetMac();
+            byte[] tail = new byte[C.Length - P.Length];
+            Array.Copy(C, P.Length, tail, 0, tail.Length);
+
+            if (!AreEqual(encT, tail))
+            {
+                Fail("stream contained wrong mac in randomised test");
+            }
+
+            cipher.Init(false, parameters);
+            byte[] decP = new byte[cipher.GetOutputSize(C.Length)];
+            predicted = cipher.GetUpdateOutputSize(C.Length);
+
+            split = NextInt(srng, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            len = cipher.ProcessBytes(C, 0, C.Length, decP, 0);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            if (predicted != len)
+            {
+                Fail("decryption reported incorrect update length in randomised test");
+            }
+
+            len += cipher.DoFinal(decP, len);
+
+            if (!AreEqual(P, decP))
+            {
+                Fail("incorrect decrypt in randomised test");
+            }
+
+            byte[] decT = cipher.GetMac();
+            if (!AreEqual(encT, decT))
+            {
+                Fail("decryption produced different mac from encryption");
+            }
+
+            //
+            // key reuse test
+            //
+            cipher.Init(false, AeadTestUtilities.ReuseKey(parameters));
+            decP = new byte[cipher.GetOutputSize(C.Length)];
+
+            split = NextInt(srng, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            len = cipher.ProcessBytes(C, 0, C.Length, decP, 0);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            len += cipher.DoFinal(decP, len);
+
+            if (!AreEqual(P, decP))
+            {
+                Fail("incorrect decrypt in randomised test");
+            }
+
+            decT = cipher.GetMac();
+            if (!AreEqual(encT, decT))
+            {
+                Fail("decryption produced different mac from encryption");
+            }
+        }
+
+        private void OutputSizeTests()
+        {
+            byte[] K = new byte[16];
+            byte[] A = null;
+            byte[] IV = new byte[15];
+
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, IV, A);
+            IAeadBlockCipher cipher = InitOcbCipher(true, parameters);
+
+            if (cipher.GetUpdateOutputSize(0) != 0)
+            {
+                Fail("incorrect getUpdateOutputSize for initial 0 bytes encryption");
+            }
+
+            if (cipher.GetOutputSize(0) != 16)
+            {
+                Fail("incorrect getOutputSize for initial 0 bytes encryption");
+            }
+
+            cipher.Init(false, parameters);
+
+            if (cipher.GetUpdateOutputSize(0) != 0)
+            {
+                Fail("incorrect getUpdateOutputSize for initial 0 bytes decryption");
+            }
+
+            // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here
+            if (cipher.GetOutputSize(0) != 0)
+            {
+                Fail("fragile getOutputSize for initial 0 bytes decryption");
+            }
+
+            if (cipher.GetOutputSize(16) != 0)
+            {
+                Fail("incorrect getOutputSize for initial MAC-size bytes decryption");
+            }
+        }
+
+        private static int NextInt(SecureRandom rand, int n)
+        {
+            if ((n & -n) == n)  // i.e., n is a power of 2
+            {
+                return (int)(((uint)n * (ulong)((uint)rand.NextInt() >> 1)) >> 31);
+            }
+
+            int bits, value;
+            do
+            {
+                bits = (int)((uint)rand.NextInt() >> 1);
+                value = bits % n;
+            }
+            while (bits - value + (n - 1) < 0);
+
+            return value;
+        }
+
         public static void Main(
             string[] args)
         {
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 8bc8e8339..ab6394571 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -20,6 +20,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             new ElGamalTest(),
             new DsaTest(),
             new ECTest(),
+            new DeterministicDsaTest(),
             new Gost3410Test(),
             new ECGost3410Test(),
             new EcIesTest(),
@@ -117,7 +118,10 @@ namespace Org.BouncyCastle.Crypto.Tests
             new SipHashTest(),
             new Poly1305Test(),
             new OcbTest(),
-            new SM3DigestTest()
+            new SM3DigestTest(),
+            new X931SignerTest(),
+            new KeccakDigestTest(),
+            new ShakeDigestTest(),
         };
 
         public static void Main(
diff --git a/crypto/test/src/crypto/test/SHA3DigestTest.cs b/crypto/test/src/crypto/test/SHA3DigestTest.cs
index 2b8ae4a63..71f51f43b 100644
--- a/crypto/test/src/crypto/test/SHA3DigestTest.cs
+++ b/crypto/test/src/crypto/test/SHA3DigestTest.cs
@@ -1,11 +1,11 @@
 using System;
+using System.IO;
+using System.Text;
 
 using NUnit.Framework;
 
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Digests;
-using Org.BouncyCastle.Crypto.Macs;
-using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
@@ -19,346 +19,218 @@ namespace Org.BouncyCastle.Crypto.Tests
     public class Sha3DigestTest
         : SimpleTest
     {
-        readonly static string[] messages = {
-            "",
-            "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67",
-            "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"
-        };
-
-        readonly static string[] digests288 = { // the default settings
-            "6753e3380c09e385d0339eb6b050a68f66cfd60a73476e6fd6adeb72f5edd7c6f04a5d01",  // message[0]    
-            "0bbe6afae0d7e89054085c1cc47b1689772c89a41796891e197d1ca1b76f288154933ded",  // message[1]
-            "82558a209b960ddeb531e6dcb281885b2400ca160472462486e79f071e88a3330a8a303d",  // message[2]
-            "94049e1ad7ef5d5b0df2b880489e7ab09ec937c3bfc1b04470e503e1ac7b1133c18f86da",  // 64k a-test
-            "a9cb5a75b5b81b7528301e72553ed6770214fa963956e790528afe420de33c074e6f4220",  // random alphabet test
-            "eadaf5ba2ad6a2f6f338fce0e1efdad2a61bb38f6be6068b01093977acf99e97a5d5827c"   // extremely long data test
-        };
-
-        readonly static string[] digests224 = {
-            "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
-            "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
-            "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab",
-            "f621e11c142fbf35fa8c22841c3a812ba1e0151be4f38d80b9f1ff53",
-            "68b5fc8c87193155bba68a2485377e809ee4f81a85ef023b9e64add0",
-            "c42e4aee858e1a8ad2976896b9d23dd187f64436ee15969afdbc68c5"
-        };
-
-        readonly static string[] digests256 = {
-            "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
-            "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
-            "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d",
-            "0047a916daa1f92130d870b542e22d3108444f5a7e4429f05762fb647e6ed9ed",
-            "db368762253ede6d4f1db87e0b799b96e554eae005747a2ea687456ca8bcbd03",
-            "5f313c39963dcf792b5470d4ade9f3a356a3e4021748690a958372e2b06f82a4"
-        };
-
-        readonly static string[] digests384 = {
-            "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
-            "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
-            "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b",
-            "c704cfe7a1a53208ca9526cd24251e0acdc252ecd978eee05acd16425cfb404ea81f5a9e2e5e97784d63ee6a0618a398",
-            "d4fe8586fd8f858dd2e4dee0bafc19b4c12b4e2a856054abc4b14927354931675cdcaf942267f204ea706c19f7beefc4",
-            "9b7168b4494a80a86408e6b9dc4e5a1837c85dd8ff452ed410f2832959c08c8c0d040a892eb9a755776372d4a8732315"
-        };
-
-        readonly static string[] digests512 = {
-            "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
-            "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
-            "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760",
-            "34341ead153aa1d1fdcf6cf624c2b4f6894b6fd16dc38bd4ec971ac0385ad54fafcb2e0ed86a1e509456f4246fdcb02c3172824cd649d9ad54c51f7fb49ea67c",
-            "dc44d4f4d36b07ab5fc04016cbe53548e5a7778671c58a43cb379fd00c06719b8073141fc22191ffc3db5f8b8983ae8341fa37f18c1c969664393aa5ceade64e",
-            "3e122edaf37398231cfaca4c7c216c9d66d5b899ec1d7ac617c40c7261906a45fc01617a021e5da3bd8d4182695b5cb785a28237cbb167590e34718e56d8aab8"
-        };
-
-        // test vectors from  http://www.di-mgt.com.au/hmac_sha3_testvectors.html
-        readonly static byte[][] macKeys =
+        internal class MySha3Digest : Sha3Digest
         {
-            Hex.Decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
-            Hex.Decode("4a656665"),
-            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
-            Hex.Decode("0102030405060708090a0b0c0d0e0f10111213141516171819"),
-            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaa"),
-            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaa"),
-            Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
-        };
-
-        readonly static string[] macData =
-        {
-            "4869205468657265",
-            "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
-            "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +
-                "dddddddddddddddddddddddddddddddddddd",
-            "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" +
-                "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
-            "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" +
-                "65204b6579202d2048617368204b6579204669727374",
-            "5468697320697320612074657374207573696e672061206c6172676572207468" +
-                "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
-                "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
-                "647320746f20626520686173686564206265666f7265206265696e6720757365" +
-                "642062792074686520484d414320616c676f726974686d2e",
-            "5468697320697320612074657374207573696e672061206c6172676572207468" +
-                "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
-                "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
-                "647320746f20626520686173686564206265666f7265206265696e6720757365\n" +
-                "642062792074686520484d414320616c676f726974686d2e"
-        };
-
-        readonly static string[] mac224 =
-        {
-            "b73d595a2ba9af815e9f2b4e53e78581ebd34a80b3bbaac4e702c4cc",
-            "e824fec96c074f22f99235bb942da1982664ab692ca8501053cbd414",
-            "770df38c99d6e2bacd68056dcfe07d4c89ae20b2686a6185e1faa449",
-            "305a8f2dfb94bad28861a03cbc4d590febe775c58cb4961c28428a0b",
-            "e7a52dfa45f95a217c100066b239aa8ad519be9b35d667268b1b57ff",
-            "ba13009405a929f398b348885caa5419191bb948ada32194afc84104",
-            "92649468be236c3c72c189909c063b13f994be05749dc91310db639e"
-        };
-
-        readonly static string[] mac256 =
-        {
-            "9663d10c73ee294054dc9faf95647cb99731d12210ff7075fb3d3395abfb9821",
-            "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1",
-            "95f43e50f8df80a21977d51a8db3ba572dcd71db24687e6f86f47c1139b26260",
-            "6331ba9b4af5804a68725b3663eb74814494b63c6093e35fb320a85d507936fd",
-            "b4d0cdee7ec2ba81a88b86918958312300a15622377929a054a9ce3ae1fac2b6",
-            "1fdc8cb4e27d07c10d897dec39c217792a6e64fa9c63a77ce42ad106ef284e02",
-            "fdaa10a0299aecff9bb411cf2d7748a4022e4a26be3fb5b11b33d8c2b7ef5484"
-        };
-
-        readonly static string[] mac384 =
-        {
-            "892dfdf5d51e4679bf320cd16d4c9dc6f749744608e003add7fba894acff87361efa4e5799be06b6461f43b60ae97048",
-            "5af5c9a77a23a6a93d80649e562ab77f4f3552e3c5caffd93bdf8b3cfc6920e3023fc26775d9df1f3c94613146ad2c9d",
-            "4243c29f2201992ff96441e3b91ff81d8c601d706fbc83252684a4bc51101ca9b2c06ddd03677303c502ac5331752a3c",
-            "b730724d3d4090cda1be799f63acbbe389fef7792fc18676fa5453aab398664650ed029c3498bbe8056f06c658e1e693",
-            "d62482ef601d7847439b55236e9679388ffcd53c62cd126f39be6ea63de762e26cd5974cb9a8de401b786b5555040f6f",
-            "4860ea191ac34994cf88957afe5a836ef36e4cc1a66d75bf77defb7576122d75f60660e4cf731c6effac06402787e2b9",
-            "fe9357e3cfa538eb0373a2ce8f1e26ad6590afdaf266f1300522e8896d27e73f654d0631c8fa598d4bb82af6b744f4f5"
-        };
-
-        readonly static string[] mac512 =
-        {
-            "8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755",
-            "c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb",
-            "eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66",
-            "b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb",
-            "d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5",
-            "2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572",
-            "6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8"
-        };
-
-        readonly static KeyParameter truncKey = new KeyParameter(Hex.Decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"));
-        readonly static byte[] truncData = Hex.Decode("546573742057697468205472756e636174696f6e");
-
-        readonly static byte[] trunc224 = Hex.Decode("f52bbcfd654264e7133085c5e69b72c3");
-        readonly static byte[] trunc256 = Hex.Decode("745e7e687f8335280d54202ef13cecc6");
-        readonly static byte[] trunc384 = Hex.Decode("fa9aea2bc1e181e47cbb8c3df243814d");
-        readonly static byte[] trunc512 = Hex.Decode("04c929fead434bba190dacfa554ce3f5");
+            internal MySha3Digest(int bitLength)
+                : base(bitLength)
+            {
+            }
 
-        readonly static byte[] xtremeData = Hex.Decode("61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f");
+            internal int MyDoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
+            {
+                return DoFinal(output, outOff, partialByte, partialBits);
+            }
+        }
 
         public override string Name
         {
-            get { return "SHA3"; }
+            get { return "SHA-3"; }
         }
 
-        private void TestDigest(IDigest digest, string[] expected)
+        public override void PerformTest()
         {
-            byte[] hash = new byte[digest.GetDigestSize()];
+            TestVectors();
+        }
 
-            for (int i = 0; i != messages.Length; i++)
+        public void TestVectors()
+        {
+            using (StreamReader r = new StreamReader(SimpleTest.GetTestDataAsStream("crypto.SHA3TestVectors.txt")))
             {
-                if (messages.Length != 0)
-                {
-                    byte[] data = Hex.Decode(messages[i]);
-
-                    digest.BlockUpdate(data, 0, data.Length);
-                }
-
-                digest.DoFinal(hash, 0);
-
-                if (!Arrays.AreEqual(Hex.Decode(expected[i]), hash))
+                String line;
+                while (null != (line = ReadLine(r)))
                 {
-                    Fail("sha3 mismatch on " + digest.AlgorithmName + " index " + i);
+                    if (line.Length != 0)
+                    {
+                        TestVector v = ReadTestVector(r, line);
+                        RunTestVector(v);
+                    }
                 }
             }
+        }
 
-            byte[] k64 = new byte[1024 * 64];
-
-            for (int i = 0; i != k64.Length; i++)
+        private MySha3Digest CreateDigest(string algorithm)
+        {
+            if (algorithm.StartsWith("SHA3-"))
             {
-                k64[i] = (byte)'a';
+                int bits = ParseDecimal(algorithm.Substring("SHA3-".Length));
+                return new MySha3Digest(bits);
             }
+            throw new ArgumentException("Unknown algorithm: " + algorithm, "algorithm");
+        }
 
-            digest.BlockUpdate(k64, 0, k64.Length);
-
-            digest.DoFinal(hash, 0);
+        private byte[] DecodeBinary(string block)
+        {
+            int bits = block.Length;
+            int fullBytes = bits / 8;
+            int totalBytes = (bits + 7) / 8;
+            byte[] result = new byte[totalBytes];
 
-            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length]), hash))
+            for (int i = 0; i < fullBytes; ++i)
             {
-                Fail("sha3 mismatch on " + digest.AlgorithmName + " 64k a");
+                string byteStr = Reverse(block.Substring(i * 8, 8));
+                result[i] = (byte)ParseBinary(byteStr);
             }
 
-            for (int i = 0; i != k64.Length; i++)
+            if (totalBytes > fullBytes)
             {
-                digest.Update((byte)'a');
+                string byteStr = Reverse(block.Substring(fullBytes * 8));
+                result[fullBytes] = (byte)ParseBinary(byteStr);
             }
 
-            digest.DoFinal(hash, 0);
+            return result;
+        }
 
-            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length]), hash))
-            {
-                Fail("sha3 mismatch on " + digest.AlgorithmName + " 64k a single");
-            }
+        private int ParseBinary(string s)
+        {
+            return new BigInteger(s, 2).IntValue;
+        }
 
+        private int ParseDecimal(string s)
+        {
+            return Int32.Parse(s);
+        }
 
-            for (int i = 0; i != k64.Length; i++)
+        private string ReadBlock(StreamReader r)
+        {
+            StringBuilder b = new StringBuilder();
+            string line;
+            while ((line = ReadBlockLine(r)) != null)
             {
-                k64[i] = (byte)('a' + (i % 26));
+                b.Append(line);
             }
+            return b.ToString();
+        }
 
-            digest.BlockUpdate(k64, 0, k64.Length);
-
-            digest.DoFinal(hash, 0);
-
-            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length + 1]), hash))
+        private string ReadBlockLine(StreamReader r)
+        {
+            string line = ReadLine(r);
+            if (line == null || line.Length == 0)
             {
-                Fail("sha3 mismatch on " + digest.AlgorithmName + " 64k alpha");
+                return null;
             }
+            return line.Replace(" ", "");
+        }
 
-            for (int i = 0; i != 64; i++)
-            {
-                digest.Update(k64[i * 1024]);
-                digest.BlockUpdate(k64, i * 1024 + 1, 1023);
-            }
+        private TestVector ReadTestVector(StreamReader r, string header)
+        {
+            string[] parts = SplitAround(header, TestVector.SAMPLE_OF);
 
-            digest.DoFinal(hash, 0);
+            string algorithm = parts[0];
+            int bits = ParseDecimal(StripFromChar(parts[1], '-'));
 
-            if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length + 1]), hash))
+            SkipUntil(r, TestVector.MSG_HEADER);
+            string messageBlock = ReadBlock(r);
+            if (messageBlock.Length != bits)
             {
-                Fail("sha3 mismatch on " + digest.AlgorithmName + " 64k chunked alpha");
+                throw new InvalidOperationException("Test vector length mismatch");
             }
+            byte[] message = DecodeBinary(messageBlock);
+
+            SkipUntil(r, TestVector.HASH_HEADER);
+            byte[] hash = Hex.Decode(ReadBlock(r));
 
-            TestDigestDoFinal(digest);
-
-            //
-            // extremely long data test
-            //
-            //Console.WriteLine("Starting very long");
-            //for (int i = 0; i != 16384; i++)
-            //{
-            //    for (int j = 0; j != 1024; j++)
-            //    {
-            //        digest.BlockUpdate(xtremeData, 0, xtremeData.Length);
-            //    }
-            //}
-    
-            //digest.DoFinal(hash, 0);
-    
-            //if (!Arrays.AreEqual(Hex.Decode(expected[messages.Length + 2]), hash))
-            //{
-            //    Fail("sha3 mismatch on " + digest.AlgorithmName + " extreme data test");
-            //}
-            //Console.WriteLine("Done");
+            return new TestVector(algorithm, bits, message, hash);
         }
 
-        private void TestDigestDoFinal(IDigest digest)
+        private string ReadLine(StreamReader r)
         {
-            byte[] hash = new byte[digest.GetDigestSize()];
-            digest.DoFinal(hash, 0);
+            string line = r.ReadLine();
+            return line == null ? null : StripFromChar(line, '#').Trim();
+        }
 
-            for (int i = 0; i <= digest.GetDigestSize(); ++i)
+        private string RequireLine(StreamReader r)
+        {
+            string line = ReadLine(r);
+            if (line == null)
             {
-                byte[] cmp = new byte[2 * digest.GetDigestSize()];
-                Array.Copy(hash, 0, cmp, i, hash.Length);
-
-                byte[] buf = new byte[2 * digest.GetDigestSize()];
-                digest.DoFinal(buf, i);
-
-                if (!Arrays.AreEqual(cmp, buf))
-                {
-                    Fail("sha3 offset DoFinal on " + digest.AlgorithmName);
-                }
+                throw new EndOfStreamException();
             }
+            return line;
         }
 
-        private void TestMac(IDigest digest, byte[][] keys, String[] data, String[] expected, byte[] truncExpected)
+        private string Reverse(string s)
         {
-            IMac mac = new HMac(digest);
-
-            for (int i = 0; i != keys.Length; i++)
-            {
-                mac.Init(new KeyParameter(keys[i]));
-
-                byte[] mData = Hex.Decode(data[i]);
+            char[] cs = s.ToCharArray();
+            Array.Reverse(cs);
+            return new string(cs);
+        }
 
-                mac.BlockUpdate(mData, 0, mData.Length);
+        private void RunTestVector(TestVector v)
+        {
+            int bits = v.Bits;
+            int partialBits = bits % 8;
 
-                byte[] macV = new byte[mac.GetMacSize()];
+            //Console.WriteLine(v.Algorithm + " " + bits + "-bit");
+            //Console.WriteLine(Hex.ToHexString(v.Message).ToUpper());
+            //Console.WriteLine(Hex.ToHexString(v.Hash).ToUpper());
 
-                mac.DoFinal(macV, 0);
+            MySha3Digest d = CreateDigest(v.Algorithm);
+            byte[] output = new byte[d.GetDigestSize()];
 
-                if (!Arrays.AreEqual(Hex.Decode(expected[i]), macV))
-                {
-                    Fail("sha3 HMAC mismatch on " + digest.AlgorithmName);
-                }
+            byte[] m = v.Message;
+            if (partialBits == 0)
+            {
+                d.BlockUpdate(m, 0, m.Length);
+                d.DoFinal(output, 0);
             }
-
+            else
             {
-                mac = new HMac(digest);
-
-                mac.Init(truncKey);
-
-                mac.BlockUpdate(truncData, 0, truncData.Length);
-
-                byte[] macV = new byte[mac.GetMacSize()];
+                d.BlockUpdate(m, 0, m.Length - 1);
+                d.MyDoFinal(output, 0, m[m.Length - 1], partialBits);
+            }
 
-                mac.DoFinal(macV, 0);
+            if (!Arrays.AreEqual(v.Hash, output))
+            {
+                Fail(v.Algorithm + " " + v.Bits + "-bit test vector hash mismatch");
+                //Console.Error.WriteLine(v.Algorithm + " " + v.Bits + "-bit test vector hash mismatch");
+                //Console.Error.WriteLine(Hex.ToHexString(output).ToUpper());
+            }
+        }
 
-                for (int i = 0; i != truncExpected.Length; i++)
-                {
-                    if (macV[i] != truncExpected[i])
-                    {
-                        Fail("mismatch on truncated HMAC for " + digest.AlgorithmName);
-                    }
-                }
+        private void SkipUntil(StreamReader r, string header)
+        {
+            string line;
+            do
+            {
+                line = RequireLine(r);
+            }
+            while (line.Length == 0);
+            if (!line.Equals(header))
+            {
+                throw new IOException("Expected: " + header);
             }
         }
 
-        public override void PerformTest()
+        private string[] SplitAround(string s, string separator)
         {
-            TestDigest(new Sha3Digest(), digests288);
-            TestDigest(new Sha3Digest(224), digests224);
-            TestDigest(new Sha3Digest(256), digests256);
-            TestDigest(new Sha3Digest(384), digests384);
-            TestDigest(new Sha3Digest(512), digests512);
-
-            TestMac(new Sha3Digest(224), macKeys, macData, mac224, trunc224);
-            TestMac(new Sha3Digest(256), macKeys, macData, mac256, trunc256);
-            TestMac(new Sha3Digest(384), macKeys, macData, mac384, trunc384);
-            TestMac(new Sha3Digest(512), macKeys, macData, mac512, trunc512);
+            int i = s.IndexOf(separator);
+            if (i < 0)
+                throw new InvalidOperationException();
+            return new string[] { s.Substring(0, i), s.Substring(i + separator.Length) };
         }
 
-        protected virtual IDigest CloneDigest(IDigest digest)
+        private string StripFromChar(string s, char c)
         {
-            return new Sha3Digest((Sha3Digest)digest);
+            int i = s.IndexOf(c);
+            if (i >= 0)
+            {
+                s = s.Substring(0, i);
+            }
+            return s;
         }
 
         public static void Main(
-            string[] args)
+            string[]    args)
         {
             RunTest(new Sha3DigestTest());
         }
@@ -370,5 +242,45 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             Assert.AreEqual(Name + ": Okay", resultText);
         }
+
+        internal class TestVector
+        {
+            internal static string SAMPLE_OF = " sample of ";
+            internal static string MSG_HEADER = "Msg as bit string";
+            internal static string HASH_HEADER = "Hash val is";
+
+            private readonly string algorithm;
+            private readonly int bits;
+            private readonly byte[] message;
+            private readonly byte[] hash;
+
+            internal TestVector(string algorithm, int bits, byte[] message, byte[] hash)
+            {
+                this.algorithm = algorithm;
+                this.bits = bits;
+                this.message = message;
+                this.hash = hash;
+            }
+
+            public string Algorithm
+            {
+                get { return algorithm; }
+            }
+
+            public int Bits
+            {
+                get { return bits; }
+            }
+
+            public byte[] Message
+            {
+                get { return message; }
+            }
+
+            public byte[] Hash
+            {
+                get { return hash; }
+            }
+        }
     }
 }
diff --git a/crypto/test/src/crypto/test/SRP6Test.cs b/crypto/test/src/crypto/test/SRP6Test.cs
index 3b80e2c16..6b64df924 100644
--- a/crypto/test/src/crypto/test/SRP6Test.cs
+++ b/crypto/test/src/crypto/test/SRP6Test.cs
@@ -23,15 +23,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        return new BigInteger(1, Hex.Decode(hex));
 	    }
 
-		// 1024 bit example prime from RFC5054 and corresponding generator
-		private static readonly BigInteger N_1024 = FromHex("EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
-	            + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
-	            + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
-	            + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A"
-	            + "FD5138FE8376435B9FC61D2FC0EB06E3");
-		private static readonly BigInteger g_1024 = BigInteger.Two;
-
-		private readonly SecureRandom random = new SecureRandom();
+        private readonly SecureRandom random = new SecureRandom();
 
 	    public override string Name
 	    {
@@ -42,9 +34,9 @@ namespace Org.BouncyCastle.Crypto.Tests
 	    {
 	    	rfc5054AppendixBTestVectors();
 
-	        testMutualVerification(N_1024, g_1024);
-	        testClientCatchesBadB(N_1024, g_1024);
-	        testServerCatchesBadA(N_1024, g_1024);
+            testMutualVerification(Srp6StandardGroups.rfc5054_1024);
+            testClientCatchesBadB(Srp6StandardGroups.rfc5054_1024);
+            testServerCatchesBadA(Srp6StandardGroups.rfc5054_1024);
 
 			testWithRandomParams(256);
 			testWithRandomParams(384);
@@ -56,8 +48,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 	    	byte[] I = Encoding.UTF8.GetBytes("alice");
 	    	byte[] P = Encoding.UTF8.GetBytes("password123");
 	    	byte[] s = Hex.Decode("BEB25379D1A8581EB5A727673A2441EE");
-	    	BigInteger N = N_1024;
-	    	BigInteger g = g_1024;
+            BigInteger N = Srp6StandardGroups.rfc5054_1024.N;
+            BigInteger g = Srp6StandardGroups.rfc5054_1024.G;
 	    	BigInteger a = FromHex("60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DDDA2D4393");
 	    	BigInteger b = FromHex("E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D105284D20");
 
@@ -148,13 +140,10 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        paramGen.Init(bits, 25, random);
 	        DHParameters parameters = paramGen.GenerateParameters();
 
-	        BigInteger g = parameters.G;
-	        BigInteger p = parameters.P;
-
-	        testMutualVerification(p, g);
+            testMutualVerification(new Srp6GroupParameters(parameters.P, parameters.G));
 		}
 
-	    private void testMutualVerification(BigInteger N, BigInteger g)
+        private void testMutualVerification(Srp6GroupParameters group)
 	    {
 	        byte[] I = Encoding.UTF8.GetBytes("username");
 	        byte[] P = Encoding.UTF8.GetBytes("password");
@@ -162,16 +151,16 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        random.NextBytes(s);
 
 	        Srp6VerifierGenerator gen = new Srp6VerifierGenerator();
-	        gen.Init(N, g, new Sha256Digest());
+	        gen.Init(group, new Sha256Digest());
 	        BigInteger v = gen.GenerateVerifier(s, I, P);
 
 	        Srp6Client client = new Srp6Client();
-	        client.Init(N, g, new Sha256Digest(), random);
+	        client.Init(group, new Sha256Digest(), random);
 
 	        Srp6Server server = new Srp6Server();
-	        server.Init(N, g, v, new Sha256Digest(), random);
+	        server.Init(group, v, new Sha256Digest(), random);
 
-	        BigInteger A = client.GenerateClientCredentials(s, I, P);
+            BigInteger A = client.GenerateClientCredentials(s, I, P);
 	        BigInteger B = server.GenerateServerCredentials();
 
 	        BigInteger clientS = client.CalculateSecret(B);
@@ -183,7 +172,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        }
 	    }
 
-	    private void testClientCatchesBadB(BigInteger N, BigInteger g)
+        private void testClientCatchesBadB(Srp6GroupParameters group)
 	    {
 	        byte[] I = Encoding.UTF8.GetBytes("username");
 	        byte[] P = Encoding.UTF8.GetBytes("password");
@@ -191,7 +180,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        random.NextBytes(s);
 
 	        Srp6Client client = new Srp6Client();
-	        client.Init(N, g, new Sha256Digest(), random);
+	        client.Init(group, new Sha256Digest(), random);
 
 	        client.GenerateClientCredentials(s, I, P);
 
@@ -207,7 +196,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 	        try
 	        {
-	        	client.CalculateSecret(N);
+	        	client.CalculateSecret(group.N);
 	        	Fail("Client failed to detect invalid value for 'B'");
 	        }
 	        catch (CryptoException)
@@ -216,7 +205,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        }
 	    }
 
-	    private void testServerCatchesBadA(BigInteger N, BigInteger g)
+        private void testServerCatchesBadA(Srp6GroupParameters group)
 	    {
 	        byte[] I = Encoding.UTF8.GetBytes("username");
 	        byte[] P = Encoding.UTF8.GetBytes("password");
@@ -224,11 +213,11 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        random.NextBytes(s);
 
 	        Srp6VerifierGenerator gen = new Srp6VerifierGenerator();
-	        gen.Init(N, g, new Sha256Digest());
+	        gen.Init(group, new Sha256Digest());
 	        BigInteger v = gen.GenerateVerifier(s, I, P);
 
 	        Srp6Server server = new Srp6Server();
-	        server.Init(N, g, v, new Sha256Digest(), random);
+	        server.Init(group, v, new Sha256Digest(), random);
 
 	        server.GenerateServerCredentials();
 
@@ -244,7 +233,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 	        try
 	        {
-	        	server.CalculateSecret(N);
+	        	server.CalculateSecret(group.N);
 	        	Fail("Client failed to detect invalid value for 'A'");
 	        }
 	        catch (CryptoException)
diff --git a/crypto/test/src/crypto/test/ShakeDigestTest.cs b/crypto/test/src/crypto/test/ShakeDigestTest.cs
new file mode 100644
index 000000000..ef4696739
--- /dev/null
+++ b/crypto/test/src/crypto/test/ShakeDigestTest.cs
@@ -0,0 +1,290 @@
+using System;
+using System.IO;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    /**
+     * SHAKE Digest Test
+     */
+    [TestFixture]
+    public class ShakeDigestTest
+        : SimpleTest
+    {
+        internal class MyShakeDigest : ShakeDigest
+        {
+            internal MyShakeDigest(int bitLength)
+                : base(bitLength)
+            {
+            }
+
+            internal int MyDoFinal(byte[] output, int outOff, int outLen, byte partialByte, int partialBits)
+            {
+                return DoFinal(output, outOff, outLen, partialByte, partialBits);
+            }
+        }
+
+        public override string Name
+        {
+            get { return "SHAKE"; }
+        }
+
+        public override void PerformTest()
+        {
+            TestVectors();
+        }
+
+        public void TestVectors()
+        {
+            using (StreamReader r = new StreamReader(SimpleTest.GetTestDataAsStream("crypto.SHAKETestVectors.txt")))
+            {
+                String line;
+                while (null != (line = ReadLine(r)))
+                {
+                    if (line.Length != 0)
+                    {
+                        TestVector v = ReadTestVector(r, line);
+                        RunTestVector(v);
+                    }
+                }
+            }
+        }
+
+        private MyShakeDigest CreateDigest(string algorithm)
+        {
+            if (algorithm.StartsWith("SHAKE-"))
+            {
+                int bits = ParseDecimal(algorithm.Substring("SHAKE-".Length));
+                return new MyShakeDigest(bits);
+            }
+            throw new ArgumentException("Unknown algorithm: " + algorithm, "algorithm");
+        }
+
+        private byte[] DecodeBinary(string block)
+        {
+            int bits = block.Length;
+            int fullBytes = bits / 8;
+            int totalBytes = (bits + 7) / 8;
+            byte[] result = new byte[totalBytes];
+
+            for (int i = 0; i < fullBytes; ++i)
+            {
+                string byteStr = Reverse(block.Substring(i * 8, 8));
+                result[i] = (byte)ParseBinary(byteStr);
+            }
+
+            if (totalBytes > fullBytes)
+            {
+                string byteStr = Reverse(block.Substring(fullBytes * 8));
+                result[fullBytes] = (byte)ParseBinary(byteStr);
+            }
+
+            return result;
+        }
+
+        private int ParseBinary(string s)
+        {
+            return new BigInteger(s, 2).IntValue;
+        }
+
+        private int ParseDecimal(string s)
+        {
+            return Int32.Parse(s);
+        }
+
+        private string ReadBlock(StreamReader r)
+        {
+            StringBuilder b = new StringBuilder();
+            string line;
+            while ((line = ReadBlockLine(r)) != null)
+            {
+                b.Append(line);
+            }
+            return b.ToString();
+        }
+
+        private string ReadBlockLine(StreamReader r)
+        {
+            string line = ReadLine(r);
+            if (line == null || line.Length == 0)
+            {
+                return null;
+            }
+            return line.Replace(" ", "");
+        }
+
+        private TestVector ReadTestVector(StreamReader r, string header)
+        {
+            string[] parts = SplitAround(header, TestVector.SAMPLE_OF);
+
+            string algorithm = parts[0];
+            int bits = ParseDecimal(StripFromChar(parts[1], '-'));
+
+            SkipUntil(r, TestVector.MSG_HEADER);
+            string messageBlock = ReadBlock(r);
+            if (messageBlock.Length != bits)
+            {
+                throw new InvalidOperationException("Test vector length mismatch");
+            }
+            byte[] message = DecodeBinary(messageBlock);
+
+            SkipUntil(r, TestVector.OUTPUT_HEADER);
+            byte[] output = Hex.Decode(ReadBlock(r));
+
+            return new TestVector(algorithm, bits, message, output);
+        }
+
+        private string ReadLine(StreamReader r)
+        {
+            string line = r.ReadLine();
+            return line == null ? null : StripFromChar(line, '#').Trim();
+        }
+
+        private string RequireLine(StreamReader r)
+        {
+            string line = ReadLine(r);
+            if (line == null)
+            {
+                throw new EndOfStreamException();
+            }
+            return line;
+        }
+
+        private string Reverse(string s)
+        {
+            char[] cs = s.ToCharArray();
+            Array.Reverse(cs);
+            return new string(cs);
+        }
+
+        private void RunTestVector(TestVector v)
+        {
+            int bits = v.Bits;
+            int partialBits = bits % 8;
+
+            byte[] expected = v.Output;
+
+            //Console.WriteLine(v.Algorithm + " " + bits + "-bit");
+            //Console.WriteLine(Hex.ToHexString(v.Message).ToUpper());
+            //Console.WriteLine(Hex.ToHexString(expected).ToUpper());
+
+            int outLen = expected.Length;
+
+            MyShakeDigest d = CreateDigest(v.Algorithm);
+            byte[] output = new byte[outLen];
+
+            byte[] m = v.Message;
+            if (partialBits == 0)
+            {
+                d.BlockUpdate(m, 0, m.Length);
+                d.DoFinal(output, 0, outLen);
+            }
+            else
+            {
+                d.BlockUpdate(m, 0, m.Length - 1);
+                d.MyDoFinal(output, 0, outLen, m[m.Length - 1], partialBits);
+            }
+
+            if (!Arrays.AreEqual(expected, output))
+            {
+                Fail(v.Algorithm + " " + v.Bits + "-bit test vector hash mismatch");
+                //Console.Error.WriteLine(v.Algorithm + " " + v.Bits + "-bit test vector hash mismatch");
+                //Console.Error.WriteLine(Hex.ToHexString(output).ToUpper());
+            }
+        }
+
+        private void SkipUntil(StreamReader r, string header)
+        {
+            string line;
+            do
+            {
+                line = RequireLine(r);
+            }
+            while (line.Length == 0);
+            if (!line.Equals(header))
+            {
+                throw new IOException("Expected: " + header);
+            }
+        }
+
+        private string[] SplitAround(string s, string separator)
+        {
+            int i = s.IndexOf(separator);
+            if (i < 0)
+                throw new InvalidOperationException();
+            return new string[] { s.Substring(0, i), s.Substring(i + separator.Length) };
+        }
+
+        private string StripFromChar(string s, char c)
+        {
+            int i = s.IndexOf(c);
+            if (i >= 0)
+            {
+                s = s.Substring(0, i);
+            }
+            return s;
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new ShakeDigestTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+
+        internal class TestVector
+        {
+            internal static string SAMPLE_OF = " sample of ";
+            internal static string MSG_HEADER = "Msg as bit string";
+            internal static string OUTPUT_HEADER = "Output val is";
+
+            private readonly string algorithm;
+            private readonly int bits;
+            private readonly byte[] message;
+            private readonly byte[] output;
+
+            internal TestVector(string algorithm, int bits, byte[] message, byte[] output)
+            {
+                this.algorithm = algorithm;
+                this.bits = bits;
+                this.message = message;
+                this.output = output;
+            }
+
+            public string Algorithm
+            {
+                get { return algorithm; }
+            }
+
+            public int Bits
+            {
+                get { return bits; }
+            }
+
+            public byte[] Message
+            {
+                get { return message; }
+            }
+
+            public byte[] Output
+            {
+                get { return output; }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/X931SignerTest.cs b/crypto/test/src/crypto/test/X931SignerTest.cs
new file mode 100644
index 000000000..d03cbc8e4
--- /dev/null
+++ b/crypto/test/src/crypto/test/X931SignerTest.cs
@@ -0,0 +1,145 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	[TestFixture]
+	public class X931SignerTest
+		:   SimpleTest
+    {
+        public override string Name
+        {
+            get { return "X931Signer"; }
+        }
+
+        public override void PerformTest()
+        {
+            BigInteger rsaPubMod = new BigInteger(Base64.Decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+            BigInteger rsaPubExp = new BigInteger(Base64.Decode("EQ=="));
+            BigInteger rsaPrivMod = new BigInteger(Base64.Decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+            BigInteger rsaPrivDP = new BigInteger(Base64.Decode("JXzfzG5v+HtLJIZqYMUefJfFLu8DPuJGaLD6lI3cZ0babWZ/oPGoJa5iHpX4Ul/7l3s1PFsuy1GhzCdOdlfRcQ=="));
+            BigInteger rsaPrivDQ = new BigInteger(Base64.Decode("YNdJhw3cn0gBoVmMIFRZzflPDNthBiWy/dUMSRfJCxoZjSnr1gysZHK01HteV1YYNGcwPdr3j4FbOfri5c6DUQ=="));
+            BigInteger rsaPrivExp = new BigInteger(Base64.Decode("DxFAOhDajr00rBjqX+7nyZ/9sHWRCCp9WEN5wCsFiWVRPtdB+NeLcou7mWXwf1Y+8xNgmmh//fPV45G2dsyBeZbXeJwB7bzx9NMEAfedchyOwjR8PYdjK3NpTLKtZlEJ6Jkh4QihrXpZMO4fKZWUm9bid3+lmiq43FwW+Hof8/E="));
+            BigInteger rsaPrivP = new BigInteger(Base64.Decode("AJ9StyTVW+AL/1s7RBtFwZGFBgd3zctBqzzwKPda6LbtIFDznmwDCqAlIQH9X14X7UPLokCDhuAa76OnDXb1OiE="));
+            BigInteger rsaPrivQ = new BigInteger(Base64.Decode("AM3JfD79dNJ5A3beScSzPtWxx/tSLi0QHFtkuhtSizeXdkv5FSba7lVzwEOGKHmW829bRoNxThDy4ds1IihW1w0="));
+            BigInteger rsaPrivQinv = new BigInteger(Base64.Decode("Lt0g7wrsNsQxuDdB8q/rH8fSFeBXMGLtCIqfOec1j7FEIuYA/ACiRDgXkHa0WgN7nLXSjHoy630wC5Toq8vvUg=="));
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, rsaPubMod, rsaPubExp);
+            RsaPrivateCrtKeyParameters rsaPrivate = new RsaPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv);
+
+            byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+            signer.Init(true, rsaPrivate);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            byte[] sig = signer.GenerateSignature();
+
+            signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+            signer.Init(false, rsaPublic);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("X9.31 Signer failed.");
+            }
+
+            ShouldPassSignatureTest1();
+            ShouldPassSignatureTest2();
+            ShouldPassSignatureTest3();
+        }
+
+        private void ShouldPassSignatureTest1()
+        {
+            BigInteger n = new BigInteger("c9be1b28f8caccca65d86cc3c9bbcc13eccc059df3b80bd2292b811eff3aa0dd75e1e85c333b8e3fa9bed53bb20f5359ff4e6900c5e9a388e3a4772a583a79e2299c76582c2b27694b65e9ba22e66bfb817f8b70b22206d7d8ae488c86dbb7137c26d5eff9b33c90e6cee640630313b7a715802e15142fef498c404a8de19674974785f0f852e2d470fe85a2e54ffca9f5851f672b71df691785a5cdabe8f14aa628942147de7593b2cf962414a5b59c632c4e14f1768c0ab2e9250824beea60a3529f11bf5e070ce90a47686eb0be1086fb21f0827f55295b4a48307db0b048c05a4aec3f488c576ca6f1879d354224c7e84cbcd8e76dd217a3de54dba73c35", 16);
+            BigInteger e = new BigInteger("e75b1b", 16);
+            byte[] msg = Hex.Decode("5bb0d1c0ef9b5c7af2477fe08d45523d3842a4b2db943f7033126c2a7829bacb3d2cfc6497ec91688189e81b7f8742488224ba320ce983ce9480722f2cc5bc42611f00bb6311884f660ccc244788378673532edb05284fd92e83f6f6dab406209032e6af9a33c998677933e32d6fb95fd27408940d7728f9c9c40267ca1d20ce");
+            byte[] sig = Hex.Decode("0fe8bb8e3109a1eb7489ef35bf4c1a0780071da789c8bd226a4170538eafefdd30b732d628f0e87a0b9450051feae9754d4fb61f57862d10f0bacc4f660d13281d0cd1141c006ade5186ff7d961a4c6cd0a4b352fc1295c5afd088f80ac1f8e192ef116a010a442655fe8ff5eeacea15807906fb0f0dfa86e680d4c005872357f7ece9aa4e20b15d5f709b30f08648ecaa34f2fbf54eb6b414fa2ff6f87561f70163235e69ccb4ac82a2e46d3be214cc2ef5263b569b2d8fd839b21a9e102665105ea762bda25bb446cfd831487a6b846100dee113ae95ae64f4af22c428c87bab809541c962bb3a56d4c86588e0af4ebc7fcc66dadced311051356d3ea745f7");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 1 failed.");
+            }
+        }
+
+        private void ShouldPassSignatureTest2()
+        {
+            BigInteger n = new BigInteger("b746ba6c3c0be64bbe33aa55b2929b0af4e86d773d44bfe5914db9287788c4663984b61a418d2eecca30d752ff6b620a07ec72eeb2b422d2429da352407b99982800b9dd7697be6a7b1baa98ca5f4fc2fe33400f20b9dba337ac25c987804165d4a6e0ee4d18eabd6de5abdfe578cae6713ff91d16c80a5bb20217fe614d9509e75a43e1825327b9da8f0a9f6eeaa1c04b69fb4bacc073569fff4ab491becbe6d0441d437fc3fa823239c4a0f75321666b68dd3f66e2dd394089a15bcc288a68a4eb0a48e17d639743b9dea0a91cc35820544732aff253f8ca9967c609dc01c2f8cd0313a7a91cfa94ff74289a1d2b6f19d1811f4b9a65f4cce9e5759b4cc64f", 16);
+            BigInteger e = new BigInteger("dcbbdb", 16);
+            byte[] msg = Hex.Decode("a5d3c8a060f897bbbc20ae0955052f37fbc70986b6e11c65075c9f457142bfa93856897c69020aa81a91b5e4f39e05cdeecc63395ab849c8262ca8bc5c96870aecb8edb0aba0024a9bdb71e06de6100344e5c318bc979ef32b8a49a8278ba99d4861bce42ebbc5c8c666aaa6cac39aff8779f2cae367620f9edd4cb1d80b6c8c");
+            byte[] sig = Hex.Decode("39fbbd1804c689a533b0043f84da0f06081038c0fbf31e443e46a05e58f50de5198bbca40522afefaba3aed7082a6cb93b1da39f1f5a42246bf64930781948d300549bef0f8d554ecfca60a1b1ecba95a7014ee4545ad4f0c4e3a31942c6738b4ccd6244b6a21267dadf0826a5f713f13b1f5a9ab8501d957a26d4948278ac67851071a315674bdab173bfef2c2690c8373da6bf3d69f30c0e5da8883de872f59521b40793854085641adf98d13db991c5d0a8aaa0222934fa33332e90ef0b954e195cb267d6ffb36c96e14d1ec7b915a87598b4461a3146566354dc2ae748c84ee0cd46543b53ebff8cdf47725b280a1f799fb6ebb4a31ad2bdd5178250f83a");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha224Digest());
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 2 failed.");
+            }
+        }
+
+        private void ShouldPassSignatureTest3()
+        {
+            BigInteger n = new BigInteger("dcb5686a3d2063a3f9cf7b9b32d2d3765b4c449b09b4960245a9111cd3b0cbd3260496885b8e1fa5db33b03efcc759d9c1afe29d93c6faebc7e0efada334b5b9a29655e2da2c8f11103d8203be311feab7ae88e9f1b2ec7d8fc655d77202b1681dd9717ec0f525b35584987e19539635a1ed23ca482a00149c609a23dc1645fd", 16);
+            BigInteger e = new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc9f7", 16);
+            BigInteger d = new BigInteger("189d6345099098992e0c9ca5f281e1338092342fa0acc85cc2a111f30f9bd2fb4753cd1a48ef0ddca9bf1af33ec76fb2e23a9fb4896c26f2235b516f7c05ef7ae81e70f4b491a5fedba9b935e9c76d761a813ce7776ff8a1e5efe1166ff2eca26aa900da88c908d51af9de26977fe39719cc781df32216fa41b838f0c63803c3", 16);
+
+            byte[] msg = Hex.Decode("911475c6e210ef4ac65b6fe8d2bfe5e01b959771b137c4ef69b88716e0d2ff9ebc1fad0f358c1dd7d50cc99a7b893ac9a6207076f08d8467d9e48c69c683bfe64a44dabaa3f7c243880f6ab7229bf7bb587822314fc5de5131983bfb2eef8b4bc1eac36f353724b567cd1ae8cddd64ddb7057549d5c81ad5fa3b5e751f00abf5");
+            byte[] sig = Hex.Decode("02c50ec0ac8a7f38ef5630c396964d6a6daaa7e3083ab5b57fa2a2632f3b70e2e85c8456cd774d45d7e44fcb063f0f04fff9f1e3adfda11272535a92cb59320b190b5ee4261f23d6ceaa925df3a7bfa42e26bf61ea9645d9d64b3c90a820802768a6e209c9f83705375a3867afccc037e8242a98fa4c3db6b2d9877754d47289");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+
+            signer.Init(true, new RsaKeyParameters(true, n, d));
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            byte[] s = signer.GenerateSignature();
+
+            if (!Arrays.AreEqual(sig, s))
+            {
+                Fail("RSA X931 sig test 3 failed.");
+            }
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 3 failed.");
+            }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new X931SignerTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/ByteQueueStreamTest.cs b/crypto/test/src/crypto/tls/test/ByteQueueStreamTest.cs
new file mode 100644
index 000000000..1d68a5215
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/ByteQueueStreamTest.cs
@@ -0,0 +1,134 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class ByteQueueStreamTest
+    {
+        [Test]
+        public void TestAvailable()
+        {
+            ByteQueueStream input = new ByteQueueStream();
+
+            // buffer is empty
+            Assert.AreEqual(0, input.Available);
+
+            // after adding once
+            input.Write(new byte[10]);
+            Assert.AreEqual(10, input.Available);
+
+            // after adding more than once
+            input.Write(new byte[5]);
+            Assert.AreEqual(15, input.Available);
+
+            // after reading a single byte
+            input.ReadByte();
+            Assert.AreEqual(14, input.Available);
+
+            // after reading into a byte array
+            input.Read(new byte[4]);
+            Assert.AreEqual(10, input.Available);
+
+            input.Close(); // so compiler doesn't whine about a resource leak
+        }
+
+        [Test]
+        public void TestSkip()
+        {
+            ByteQueueStream input = new ByteQueueStream();
+
+            // skip when buffer is empty
+            Assert.AreEqual(0, input.Skip(10));
+
+            // skip equal to available
+            input.Write(new byte[2]);
+            Assert.AreEqual(2, input.Skip(2));
+            Assert.AreEqual(0, input.Available);
+
+            // skip less than available
+            input.Write(new byte[10]);
+            Assert.AreEqual(5, input.Skip(5));
+            Assert.AreEqual(5, input.Available);
+
+            // skip more than available
+            Assert.AreEqual(5, input.Skip(20));
+            Assert.AreEqual(0, input.Available);
+
+            input.Close();// so compiler doesn't whine about a resource leak
+        }
+
+        [Test]
+        public void TestRead()
+        {
+            ByteQueueStream input = new ByteQueueStream();
+            input.Write(new byte[] { 0x01, 0x02 });
+            input.Write(new byte[]{ 0x03 });
+
+            Assert.AreEqual(0x01, input.ReadByte());
+            Assert.AreEqual(0x02, input.ReadByte());
+            Assert.AreEqual(0x03, input.ReadByte());
+            Assert.AreEqual(-1, input.ReadByte());
+
+            input.Close(); // so compiler doesn't whine about a resource leak
+        }
+
+        [Test]
+        public void TestReadArray()
+        {
+            ByteQueueStream input = new ByteQueueStream();
+            input.Write(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+
+            byte[] buffer = new byte[5];
+
+            // read less than available into specified position
+            Assert.AreEqual(1, input.Read(buffer, 2, 1));
+            AssertArrayEquals(new byte[]{ 0x00, 0x00, 0x01, 0x00, 0x00 }, buffer);
+
+            // read equal to available
+            Assert.AreEqual(5, input.Read(buffer));
+            AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+
+            // read more than available
+            input.Write(new byte[]{ 0x01, 0x02, 0x03 });
+            Assert.AreEqual(3, input.Read(buffer));
+            AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer);
+
+            input.Close(); // so compiler doesn't whine about a resource leak
+        }
+
+        [Test]
+        public void TestPeek()
+        {
+            ByteQueueStream input = new ByteQueueStream();
+
+            byte[] buffer = new byte[5];
+
+            // peek more than available
+            Assert.AreEqual(0, input.Peek(buffer));
+            AssertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }, buffer);
+
+            // peek less than available
+            input.Write(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+            Assert.AreEqual(5, input.Peek(buffer));
+            AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05 }, buffer);
+            Assert.AreEqual(6, input.Available);
+
+            // peek equal to available
+            input.ReadByte();
+            Assert.AreEqual(5, input.Peek(buffer));
+            AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+            Assert.AreEqual(5, input.Available);
+
+            input.Close(); // so compiler doesn't whine about a resource leak
+        }
+
+        private static void AssertArrayEquals(byte[] a, byte[] b)
+        {
+            Assert.IsTrue(Arrays.AreEqual(a, b));
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs b/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs
new file mode 100644
index 000000000..bc99ccc63
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            Server server = new Server(serverProtocol, network.Server);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            DatagramTransport clientTransport = network.Client;
+
+            clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0);
+
+            clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+
+            MockDtlsClient client = new MockDtlsClient(null);
+
+            DtlsTransport dtlsClient = clientProtocol.Connect(client, clientTransport);
+
+            for (int i = 1; i <= 10; ++i)
+            {
+                byte[] data = new byte[i];
+                Arrays.Fill(data, (byte)i);
+                dtlsClient.Send(data, 0, data.Length);
+            }
+
+            byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+            while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+            {
+            }
+
+            dtlsClient.Close();
+
+            server.Shutdown(serverThread);
+        }
+
+        internal class Server
+        {
+            private readonly DtlsServerProtocol mServerProtocol;
+            private readonly DatagramTransport mServerTransport;
+            private volatile bool isShutdown = false;
+
+            internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            {
+                this.mServerProtocol = serverProtocol;
+                this.mServerTransport = serverTransport;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockDtlsServer server = new MockDtlsServer();
+                    DtlsTransport dtlsServer = mServerProtocol.Accept(server, mServerTransport);
+                    byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+                    while (!isShutdown)
+                    {
+                        int length = dtlsServer.Receive(buf, 0, buf.Length, 1000);
+                        if (length >= 0)
+                        {
+                            dtlsServer.Send(buf, 0, length);
+                        }
+                    }
+                    dtlsServer.Close();
+                }
+                catch (Exception e)
+                {
+                    Console.Error.WriteLine(e.StackTrace);
+                }
+            }
+
+            internal void Shutdown(Thread serverThread)
+            {
+                if (!isShutdown)
+                {
+                    isShutdown = true;
+                    serverThread.Join();
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
new file mode 100644
index 000000000..d4af04fac
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsTestCase
+    {
+        private static void CheckDtlsVersion(ProtocolVersion version)
+        {
+            if (version != null && !version.IsDtls)
+                throw new InvalidOperationException("Non-DTLS version");
+        }
+
+        [Test, TestCaseSource(typeof(DtlsTestSuite), "Suite")]
+        public void RunTest(TlsTestConfig config)
+        {
+            CheckDtlsVersion(config.clientMinimumVersion);
+            CheckDtlsVersion(config.clientOfferVersion);
+            CheckDtlsVersion(config.serverMaximumVersion);
+            CheckDtlsVersion(config.serverMinimumVersion);
+
+            SecureRandom secureRandom = new SecureRandom();
+
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+            TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+            Server server = new Server(this, serverProtocol, network.Server, serverImpl);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            Exception caught = null;
+            try
+            {
+                DatagramTransport clientTransport = network.Client;
+
+                if (TlsTestConfig.DEBUG)
+                {
+                    clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+                }
+
+                DtlsTransport dtlsClient = clientProtocol.Connect(clientImpl, clientTransport);
+
+                for (int i = 1; i <= 10; ++i)
+                {
+                    byte[] data = new byte[i];
+                    Arrays.Fill(data, (byte)i);
+                    dtlsClient.Send(data, 0, data.Length);
+                }
+    
+                byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+                while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+                {
+                }
+    
+                dtlsClient.Close();
+            }
+            catch (Exception e)
+            {
+                caught = e;
+                LogException(caught);
+            }
+
+            server.Shutdown(serverThread);
+
+            // TODO Add checks that the various streams were closed
+
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd, "Client fatal alert connection end");
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd, "Server fatal alert connection end");
+
+            Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription, "Client fatal alert description");
+            Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription, "Server fatal alert description");
+
+            if (config.expectFatalAlertConnectionEnd == -1)
+            {
+                Assert.IsNull(caught, "Unexpected client exception");
+                Assert.IsNull(server.mCaught, "Unexpected server exception");
+            }
+        }
+
+        protected void LogException(Exception e)
+        {
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.Error.WriteLine(e.StackTrace);
+            }
+        }
+
+        internal class Server
+        {
+            private readonly DtlsTestCase mOuter;
+            private readonly DtlsServerProtocol mServerProtocol;
+            private readonly DatagramTransport mServerTransport;
+            private readonly TlsTestServerImpl mServerImpl;
+
+            private volatile bool isShutdown = false;
+            internal Exception mCaught = null;
+
+            internal Server(DtlsTestCase outer, DtlsServerProtocol serverProtocol, DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+            {
+                this.mOuter = outer;
+                this.mServerProtocol = serverProtocol;
+                this.mServerTransport = serverTransport;
+                this.mServerImpl = serverImpl;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    DtlsTransport dtlsServer = mServerProtocol.Accept(mServerImpl, mServerTransport);
+                    byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+                    while (!isShutdown)
+                    {
+                        int length = dtlsServer.Receive(buf, 0, buf.Length, 100);
+                        if (length >= 0)
+                        {
+                            dtlsServer.Send(buf, 0, length);
+                        }
+                    }
+                    dtlsServer.Close();
+                }
+                catch (Exception e)
+                {
+                    mCaught = e;
+                    mOuter.LogException(mCaught);
+                }
+            }
+
+            internal void Shutdown(Thread serverThread)
+            {
+                if (!isShutdown)
+                {
+                    isShutdown = true;
+                    serverThread.Interrupt();
+                    serverThread.Join();
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
new file mode 100644
index 000000000..eb9d42e5f
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class DtlsTestSuite
+    {
+        // Make the access to constants less verbose 
+        internal class C : TlsTestConfig {}
+
+        public DtlsTestSuite()
+        {
+        }
+
+        public static IEnumerable Suite()
+        {
+            IList testSuite = new ArrayList();
+
+            AddFallbackTests(testSuite);
+            AddVersionTests(testSuite, ProtocolVersion.DTLSv10);
+            AddVersionTests(testSuite, ProtocolVersion.DTLSv12);
+
+            return testSuite;
+        }
+
+        private static void AddFallbackTests(IList testSuite)
+        {
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+                c.clientFallback = true;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackGood"));
+            }
+
+            /*
+             * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+             * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+             * alerts being raised
+             */
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+            //    c.clientOfferVersion = ProtocolVersion.DTLSv10;
+            //    c.clientFallback = true;
+            //    c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+            //    testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
+            //}
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+                c.clientOfferVersion = ProtocolVersion.DTLSv10;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackNone"));
+            }
+        }
+
+        private static void AddVersionTests(IList testSuite, ProtocolVersion version)
+        {
+            string prefix = version.ToString()
+                .Replace(" ", "")
+                .Replace("\\", "")
+                .Replace(".", "")
+                + "_";
+
+            /*
+             * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+             * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+             * alerts being raised
+             */
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+            //    c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
+            //}
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+            //    c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
+            //}
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_NONE;
+            //    c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+            //    c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
+            //}
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+            }
+        }
+
+        private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion version)
+        {
+            TlsTestConfig c = new TlsTestConfig();
+            c.clientMinimumVersion = ProtocolVersion.DTLSv10;
+            /*
+             * TODO We'd like to just set the offer version to DTLSv12, but there is a known issue with
+             * overly-restrictive version checks b/w BC DTLS 1.2 client, BC DTLS 1.0 server
+             */
+            c.clientOfferVersion = version;
+            c.serverMaximumVersion = version;
+            c.serverMinimumVersion = ProtocolVersion.DTLSv10;
+            return c;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs
new file mode 100644
index 000000000..a26c5bdbf
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs
@@ -0,0 +1,86 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class LoggingDatagramTransport
+        :   DatagramTransport
+    {
+        private static readonly string HEX_CHARS = "0123456789ABCDEF";
+
+        private readonly DatagramTransport transport;
+        private readonly TextWriter output;
+        private readonly long launchTimestamp;
+
+        public LoggingDatagramTransport(DatagramTransport transport, TextWriter output)
+        {
+            this.transport = transport;
+            this.output = output;
+            this.launchTimestamp = DateTimeUtilities.CurrentUnixMs();
+        }
+
+        public virtual int GetReceiveLimit()
+        {
+            return transport.GetReceiveLimit();
+        }
+
+        public virtual int GetSendLimit()
+        {
+            return transport.GetSendLimit();
+        }
+
+        public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+        {
+            int length = transport.Receive(buf, off, len, waitMillis);
+            if (length >= 0)
+            {
+                DumpDatagram("Received", buf, off, length);
+            }
+            return length;
+        }
+
+        public virtual void Send(byte[] buf, int off, int len)
+        {
+            DumpDatagram("Sending", buf, off, len);
+            transport.Send(buf, off, len);
+        }
+
+        public virtual void Close()
+        {
+        }
+
+        private void DumpDatagram(string verb, byte[] buf, int off, int len)
+        {
+            long timestamp = DateTimeUtilities.CurrentUnixMs() - launchTimestamp;
+            StringBuilder sb = new StringBuilder("(+" + timestamp + "ms) " + verb + " " + len + " byte datagram:");
+            for (int pos = 0; pos < len; ++pos)
+            {
+                if (pos % 16 == 0)
+                {
+                    sb.Append(Environment.NewLine);
+                    sb.Append("    ");
+                }
+                else if (pos % 16 == 8)
+                {
+                    sb.Append('-');
+                }
+                else
+                {
+                    sb.Append(' ');
+                }
+                int val = buf[off + pos] & 0xFF;
+                sb.Append(HEX_CHARS[val >> 4]);
+                sb.Append(HEX_CHARS[val & 0xF]);
+            }
+            Dump(sb.ToString());
+        }
+
+        private void Dump(string s)
+        {
+            lock (this) output.WriteLine(s);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs b/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
new file mode 100644
index 000000000..48df36ca9
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Net;
+using System.Threading;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDatagramAssociation
+    {
+        private int mtu;
+        private MockDatagramTransport client, server;
+
+        public MockDatagramAssociation(int mtu)
+        {
+            this.mtu = mtu;
+
+            IList clientQueue = new ArrayList();
+            IList serverQueue = new ArrayList();
+
+            this.client = new MockDatagramTransport(this, clientQueue, serverQueue);
+            this.server = new MockDatagramTransport(this, serverQueue, clientQueue);
+        }
+
+        public virtual DatagramTransport Client
+        {
+            get { return client; }
+        }
+
+        public virtual DatagramTransport Server
+        {
+            get { return server; }
+        }
+
+        private class MockDatagramTransport
+            :   DatagramTransport
+        {
+            private readonly MockDatagramAssociation mOuter;
+
+            private IList receiveQueue, sendQueue;
+
+            internal MockDatagramTransport(MockDatagramAssociation outer, IList receiveQueue, IList sendQueue)
+            {
+                this.mOuter = outer;
+                this.receiveQueue = receiveQueue;
+                this.sendQueue = sendQueue;
+            }
+
+            public virtual int GetReceiveLimit()
+            {
+                return mOuter.mtu;
+            }
+
+            public virtual int GetSendLimit()
+            {
+                return mOuter.mtu;
+            }
+
+            public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+            {
+                lock (receiveQueue)
+                {
+                    if (receiveQueue.Count < 1)
+                    {
+                        try
+                        {
+                            Monitor.Wait(receiveQueue, waitMillis);
+                        }
+                        catch (ThreadInterruptedException)
+                        {
+                            // TODO Keep waiting until full wait expired?
+                        }
+                        if (receiveQueue.Count < 1)
+                        {
+                            return -1;
+                        }
+                    }
+                    byte[] packet = (byte[])receiveQueue[0];
+                    receiveQueue.RemoveAt(0);
+                    int copyLength = System.Math.Min(len, packet.Length);
+                    Array.Copy(packet, 0, buf, off, copyLength);
+                    return copyLength;
+                }
+            }
+
+            public virtual void Send(byte[] buf, int off, int len)
+            {
+                if (len > mOuter.mtu)
+                {
+                    // TODO Simulate rejection?
+                }
+
+                byte[] packet = Arrays.CopyOfRange(buf, off, off + len);
+
+                lock (sendQueue)
+                {
+                    sendQueue.Add(packet);
+                    Monitor.PulseAll(sendQueue);
+                }
+            }
+
+            public virtual void Close()
+            {
+                // TODO?
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
new file mode 100644
index 000000000..e3c604db7
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDtlsClient
+        :   DefaultTlsClient
+    {
+        protected TlsSession mSession;
+
+        public MockDtlsClient(TlsSession session)
+        {
+            this.mSession = session;
+        }
+
+        public override TlsSession GetSessionToResume()
+        {
+            return this.mSession;
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS client raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override ProtocolVersion ClientVersion
+        {
+            get { return ProtocolVersion.DTLSv12; }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.DTLSv10; }
+        }
+
+        //public override int[] GetCipherSuites()
+        //{
+        //    return Arrays.Concatenate(base.GetCipherSuites(),
+        //        new int[]
+        //        {
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+        //        });
+        //}
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
+            TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+            TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("Negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            TlsSession newSession = mContext.ResumableSession;
+            if (newSession != null)
+            {
+                byte[] newSessionID = newSession.SessionID;
+                string hex = Hex.ToHexString(newSessionID);
+
+                if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+                {
+                    Console.WriteLine("Resumed session: " + hex);
+                }
+                else
+                {
+                    Console.WriteLine("Established session: " + hex);
+                }
+
+                this.mSession = newSession;
+            }
+        }
+
+        internal class MyTlsAuthentication
+            :   TlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public virtual void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("DTLS client received server certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+
+            public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                byte[] certificateTypes = certificateRequest.CertificateTypes;
+                if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+                    return null;
+
+                return TlsTestUtilities.LoadSignerCredentials(mContext, certificateRequest.SupportedSignatureAlgorithms,
+                    SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsServer.cs b/crypto/test/src/crypto/tls/test/MockDtlsServer.cs
new file mode 100644
index 000000000..19062181b
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDtlsServer.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDtlsServer
+        :   DefaultTlsServer
+    {
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS server raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return Arrays.Concatenate(base.GetCipherSuites(),
+                new int[]
+                {
+                    CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+                });
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+            IList serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+            }
+
+            IList certificateAuthorities = new ArrayList();
+            certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
+
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            X509CertificateStructure[] chain = clientCertificate.GetCertificateList();
+            Console.WriteLine("DTLS server received client certificate chain of length " + chain.Length);
+            for (int i = 0; i != chain.Length; i++)
+            {
+                X509CertificateStructure entry = chain[i];
+                // TODO Create fingerprint based on certificate signature algorithm digest
+                Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                    + entry.Subject + ")");
+            }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.DTLSv12; }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.DTLSv10; }
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[] { "x509-server.pem", "x509-ca.pem" },
+                "x509-server-key.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs b/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
new file mode 100644
index 000000000..dfc0e93a0
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockPskTlsClient
+        :   PskTlsClient
+    {
+        internal TlsSession mSession;
+
+        internal MockPskTlsClient(TlsSession session)
+            :   this(session, new BasicTlsPskIdentity("client", new byte[16]))
+        {
+        }
+
+        internal MockPskTlsClient(TlsSession session, TlsPskIdentity pskIdentity)
+            :   base(pskIdentity)
+        {
+            this.mSession = session;
+        }
+
+        public override TlsSession GetSessionToResume()
+        {
+            return this.mSession;
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-PSK client raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-PSK client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            TlsSession newSession = mContext.ResumableSession;
+            if (newSession != null)
+            {
+                byte[] newSessionID = newSession.SessionID;
+                string hex = Hex.ToHexString(newSessionID);
+
+                if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+                {
+                    Console.WriteLine("Resumed session: " + hex);
+                }
+                else
+                {
+                    Console.WriteLine("Established session: " + hex);
+                }
+
+                this.mSession = newSession;
+            }
+        }
+
+        public override int[] GetCipherSuites()
+        {
+            return new int[]{ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA };
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+	        get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("TLS-PSK client negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        internal class MyTlsAuthentication
+            :   ServerOnlyTlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public override void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("TLS-PSK client received server certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockPskTlsServer.cs b/crypto/test/src/crypto/tls/test/MockPskTlsServer.cs
new file mode 100644
index 000000000..7394a2077
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockPskTlsServer.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockPskTlsServer
+        :   PskTlsServer
+    {
+        internal MockPskTlsServer()
+            :   base(new MyIdentityManager())
+        {
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-PSK server raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-PSK server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            byte[] pskIdentity = mContext.SecurityParameters.PskIdentity;
+            if (pskIdentity != null)
+            {
+                string name = Strings.FromUtf8ByteArray(pskIdentity);
+                Console.WriteLine("TLS-PSK server completed handshake for PSK identity: " + name);
+            }
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return new int[]{ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA };
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override ProtocolVersion GetServerVersion()
+        {
+            ProtocolVersion serverVersion = base.GetServerVersion();
+
+            Console.WriteLine("TLS-PSK server negotiated " + serverVersion);
+
+            return serverVersion;
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{"x509-server.pem", "x509-ca.pem"},
+                "x509-server-key.pem");
+        }
+
+        internal class MyIdentityManager
+            :   TlsPskIdentityManager
+        {
+            public virtual byte[] GetHint()
+            {
+                return Strings.ToUtf8ByteArray("hint");
+            }
+
+            public virtual byte[] GetPsk(byte[] identity)
+            {
+                if (identity != null)
+                {
+                    string name = Strings.FromUtf8ByteArray(identity);
+                    if (name.Equals("client"))
+                    {
+                        return new byte[16];
+                    }
+                }
+                return null;
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs b/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
new file mode 100644
index 000000000..8a6b9f496
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockSrpTlsClient
+        :   SrpTlsClient
+    {
+        internal TlsSession mSession;
+
+        internal MockSrpTlsClient(TlsSession session, byte[] identity, byte[] password)
+            :   base(identity, password)
+        {
+            this.mSession = session;
+        }
+
+        public override TlsSession GetSessionToResume()
+        {
+            return this.mSession;
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP client raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            TlsSession newSession = mContext.ResumableSession;
+            if (newSession != null)
+            {
+                byte[] newSessionID = newSession.SessionID;
+                string hex = Hex.ToHexString(newSessionID);
+
+                if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+                {
+                    Console.WriteLine("Resumed session: " + hex);
+                }
+                else
+                {
+                    Console.WriteLine("Established session: " + hex);
+                }
+
+                this.mSession = newSession;
+            }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("TLS-SRP client negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        internal class MyTlsAuthentication
+            : ServerOnlyTlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public override void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("TLS-SRP client received server certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs b/crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs
new file mode 100644
index 000000000..c15f63e0b
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockSrpTlsServer
+        :   SrpTlsServer
+    {
+        internal static readonly Srp6GroupParameters TEST_GROUP = Srp6StandardGroups.rfc5054_1024;
+        internal static readonly byte[] TEST_IDENTITY = Strings.ToUtf8ByteArray("client");
+        internal static readonly byte[] TEST_PASSWORD = Strings.ToUtf8ByteArray("password");
+        internal static readonly byte[] TEST_SALT = Strings.ToUtf8ByteArray("salt");
+        internal static readonly byte[] TEST_SEED_KEY = Strings.ToUtf8ByteArray("seed_key");
+
+        internal MockSrpTlsServer()
+            :   base(new MyIdentityManager())
+        {
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP server raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            byte[] srpIdentity = mContext.SecurityParameters.SrpIdentity;
+            if (srpIdentity != null)
+            {
+                string name = Strings.FromUtf8ByteArray(srpIdentity);
+                Console.WriteLine("TLS-SRP server completed handshake for SRP identity: " + name);
+            }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override ProtocolVersion GetServerVersion()
+        {
+            ProtocolVersion serverVersion = base.GetServerVersion();
+
+            Console.WriteLine("TLS-SRP server negotiated " + serverVersion);
+
+            return serverVersion;
+        }
+
+        protected override TlsSignerCredentials GetDsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.dsa,
+                "x509-server-dsa.pem", "x509-server-key-dsa.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+
+        internal class MyIdentityManager
+            :   TlsSrpIdentityManager
+        {
+            protected SimulatedTlsSrpIdentityManager unknownIdentityManager = SimulatedTlsSrpIdentityManager.GetRfc5054Default(
+                TEST_GROUP, TEST_SEED_KEY);
+
+            public virtual TlsSrpLoginParameters GetLoginParameters(byte[] identity)
+            {
+                if (Arrays.AreEqual(TEST_IDENTITY, identity))
+                {
+                    Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator();
+                    verifierGenerator.Init(TEST_GROUP, TlsUtilities.CreateHash(HashAlgorithm.sha1));
+
+                    BigInteger verifier = verifierGenerator.GenerateVerifier(TEST_SALT, identity, TEST_PASSWORD);
+
+                    return new TlsSrpLoginParameters(TEST_GROUP, verifier, TEST_SALT);
+                }
+
+                return unknownIdentityManager.GetLoginParameters(identity);
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
new file mode 100644
index 000000000..7c1198632
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockTlsClient
+        :   DefaultTlsClient
+    {
+        internal TlsSession mSession;
+
+        internal MockTlsClient(TlsSession session)
+        {
+            this.mSession = session;
+        }
+
+        public override TlsSession GetSessionToResume()
+        {
+            return this.mSession;
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS client raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        //public override int[] GetCipherSuites()
+        //{
+        //    return Arrays.Concatenate(base.GetCipherSuites(),
+        //        new int[]
+        //        {
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+        //        });
+        //}
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
+            TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+            TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("TLS client negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            TlsSession newSession = mContext.ResumableSession;
+            if (newSession != null)
+            {
+                byte[] newSessionID = newSession.SessionID;
+                string hex = Hex.ToHexString(newSessionID);
+
+                if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+                {
+                    Console.WriteLine("Resumed session: " + hex);
+                }
+                else
+                {
+                    Console.WriteLine("Established session: " + hex);
+                }
+
+                this.mSession = newSession;
+            }
+        }
+
+        internal class MyTlsAuthentication
+            :   TlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public virtual void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+
+            public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                byte[] certificateTypes = certificateRequest.CertificateTypes;
+                if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+                    return null;
+
+                return TlsTestUtilities.LoadSignerCredentials(mContext, certificateRequest.SupportedSignatureAlgorithms,
+                    SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockTlsServer.cs b/crypto/test/src/crypto/tls/test/MockTlsServer.cs
new file mode 100644
index 000000000..8fce95d63
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockTlsServer.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockTlsServer
+        :   DefaultTlsServer
+    {
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS server raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return Arrays.Concatenate(base.GetCipherSuites(),
+                new int[]
+                {
+                    CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+                });
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override ProtocolVersion GetServerVersion()
+        {
+            ProtocolVersion serverVersion = base.GetServerVersion();
+
+            Console.WriteLine("TLS server negotiated " + serverVersion);
+
+            return serverVersion;
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+            IList serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+            }
+
+            IList certificateAuthorities = new ArrayList();
+            certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
+
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            X509CertificateStructure[] chain = clientCertificate.GetCertificateList();
+            Console.WriteLine("TLS server received client certificate chain of length " + chain.Length);
+            for (int i = 0; i != chain.Length; i++)
+            {
+                X509CertificateStructure entry = chain[i];
+                // TODO Create fingerprint based on certificate signature algorithm digest
+                Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                    + entry.Subject + ")");
+            }
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{ "x509-server.pem", "x509-ca.pem" },
+                "x509-server-key.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/NetworkStream.cs b/crypto/test/src/crypto/tls/test/NetworkStream.cs
new file mode 100644
index 000000000..04de81e13
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/NetworkStream.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class NetworkStream
+        :   Stream
+    {
+        private readonly Stream mInner;
+        private bool mClosed = false;
+
+        internal NetworkStream(Stream inner)
+        {
+            this.mInner = inner;
+        }
+
+        internal virtual bool IsClosed
+        {
+            get { lock (this) return mClosed; }
+        }
+
+        public override bool CanRead
+        {
+            get { return mInner.CanRead; }
+        }
+
+        public override bool CanSeek
+        {
+            get { return mInner.CanSeek; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return mInner.CanWrite; }
+        }
+
+        public override void Close()
+        {
+            lock (this) mClosed = true;
+        }
+
+        public override void Flush()
+        {
+            mInner.Flush();
+        }
+
+        public override long Length
+        {
+            get { return mInner.Length; }
+        }
+
+        public override long Position
+        {
+            get { return mInner.Position; }
+            set { mInner.Position = value; }
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            return mInner.Seek(offset, origin);
+        }
+
+        public override void SetLength(long value)
+        {
+            mInner.SetLength(value);
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            CheckNotClosed();
+            return mInner.Read(buffer, offset, count);
+        }
+
+        public override int ReadByte()
+        {
+            CheckNotClosed();
+            return mInner.ReadByte();
+        }
+
+        public override void Write(byte[] buf, int off, int len)
+        {
+            CheckNotClosed();
+            mInner.Write(buf, off, len);
+        }
+
+        public override void WriteByte(byte value)
+        {
+            CheckNotClosed();
+            mInner.WriteByte(value);
+        }
+
+        private void CheckNotClosed()
+        {
+            lock (this)
+            {
+                if (mClosed)
+                    throw new ObjectDisposedException(this.GetType().Name);
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/PipedStream.cs b/crypto/test/src/crypto/tls/test/PipedStream.cs
new file mode 100644
index 000000000..6b2c15059
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/PipedStream.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+using System.Threading;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class PipedStream
+        :   Stream
+    {
+        private readonly MemoryStream mBuf = new MemoryStream();
+        private bool mClosed = false;
+
+        private PipedStream mOther = null;
+        private long mReadPos = 0;
+
+        internal PipedStream()
+        {
+        }
+
+        internal PipedStream(PipedStream other)
+        {
+            lock (other)
+            {
+                this.mOther = other;
+                other.mOther = this;
+            }
+        }
+
+        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()
+        {
+            lock (this)
+            {
+                mClosed = true;
+                Monitor.PulseAll(this);
+            }
+        }
+
+        public override void Flush()
+        {
+        }
+
+        public override long Length
+        {
+            get { throw new NotImplementedException(); }
+        }
+
+        public override long Position
+        {
+            get { throw new NotImplementedException(); }
+            set { throw new NotImplementedException(); }
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void SetLength(long value)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            lock (mOther)
+            {
+                WaitForData();
+                int len = (int)System.Math.Min(count, mOther.mBuf.Position - mReadPos);
+                Array.Copy(mOther.mBuf.GetBuffer(), mReadPos, buffer, offset, len);
+                mReadPos += len;
+                return len;
+            }
+        }
+
+        public override int ReadByte()
+        {
+            lock (mOther)
+            {
+                WaitForData();
+                bool eof = (mReadPos >= mOther.mBuf.Position);
+                return eof ? -1 : mOther.mBuf.GetBuffer()[mReadPos++];
+            }
+        }
+
+        public override void Write(byte[] buf, int off, int len)
+        {
+            lock (this)
+            {
+                CheckOpen();
+                mBuf.Write(buf, off, len);
+                Monitor.PulseAll(this);
+            }
+        }
+
+        public override void WriteByte(byte value)
+        {
+            lock (this)
+            {
+                CheckOpen();
+                mBuf.WriteByte(value);
+                Monitor.PulseAll(mBuf);
+            }
+        }
+
+        private void CheckOpen()
+        {
+            if (mClosed)
+                throw new ObjectDisposedException(this.GetType().Name);
+        }
+
+        private void WaitForData()
+        {
+            while (mReadPos >= mOther.mBuf.Position && !mOther.mClosed)
+            {
+                Monitor.Wait(mOther);
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs b/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs
new file mode 100644
index 000000000..7072c7105
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs
@@ -0,0 +1,79 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    /**
+     * A simple test designed to conduct a TLS handshake with an external TLS server.
+     * <p>
+     * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+     * this package (under 'src/test/resources') for help configuring an external TLS server.
+     * </p><p>
+     * In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys.
+     * </p>
+     */
+    public class PskTlsClientTest
+    {
+        private static readonly SecureRandom secureRandom = new SecureRandom();
+
+        public static void Main(string[] args)
+        {
+            string hostname = "localhost";
+            int port = 5556;
+
+            long time1 = DateTime.UtcNow.Ticks;
+
+            /*
+             * Note: This is the default PSK identity for 'openssl s_server' testing, the server must be
+             * started with "-psk 6161616161" to make the keys match, and possibly the "-psk_hint"
+             * option should be present.
+             */
+            string psk_identity = "Client_identity";
+            byte[] psk = new byte[]{ 0x61, 0x61, 0x61, 0x61, 0x61 };
+
+            BasicTlsPskIdentity pskIdentity = new BasicTlsPskIdentity(psk_identity, psk);
+
+            MockPskTlsClient client = new MockPskTlsClient(null, pskIdentity);
+            TlsClientProtocol protocol = OpenTlsConnection(hostname, port, client);
+            protocol.Close();
+
+            long time2 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 1: " + (time2 - time1)/TimeSpan.TicksPerMillisecond + "ms");
+
+            client = new MockPskTlsClient(client.GetSessionToResume(), pskIdentity);
+            protocol = OpenTlsConnection(hostname, port, client);
+
+            long time3 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 2: " + (time3 - time2)/TimeSpan.TicksPerMillisecond + "ms");
+
+            byte[] req = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\n\r\n");
+
+            Stream tlsStream = protocol.Stream;
+            tlsStream.Write(req, 0, req.Length);
+            tlsStream.Flush();
+
+            StreamReader reader = new StreamReader(tlsStream);
+
+            String line;
+            while ((line = reader.ReadLine()) != null)
+            {
+                Console.WriteLine(">>> " + line);
+            }
+
+            protocol.Close();
+        }
+
+        internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
+        {
+            TcpClient tcp = new TcpClient(hostname, port);
+
+            TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
+            protocol.Connect(client);
+            return protocol;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsClientTest.cs b/crypto/test/src/crypto/tls/test/TlsClientTest.cs
new file mode 100644
index 000000000..c9a5ef9ad
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsClientTest.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    /**
+     * A simple test designed to conduct a TLS handshake with an external TLS server.
+     * <p/>
+     * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+     * this package (under 'src/test/resources') for help configuring an external TLS server.
+     */
+    public class TlsClientTest
+    {
+        private static readonly SecureRandom secureRandom = new SecureRandom();
+
+        public static void Main(string[] args)
+        {
+            string hostname = "localhost";
+            int port = 5556;
+
+            long time1 = DateTime.UtcNow.Ticks;
+
+            MockTlsClient client = new MockTlsClient(null);
+            TlsClientProtocol protocol = OpenTlsConnection(hostname, port, client);
+            protocol.Close();
+
+            long time2 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 1: " + (time2 - time1)/TimeSpan.TicksPerMillisecond + "ms");
+
+            client = new MockTlsClient(client.GetSessionToResume());
+            protocol = OpenTlsConnection(hostname, port, client);
+
+            long time3 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 2: " + (time3 - time2)/TimeSpan.TicksPerMillisecond + "ms");
+
+            byte[] req = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\n\r\n");
+
+            Stream tlsStream = protocol.Stream;
+            tlsStream.Write(req, 0, req.Length);
+            tlsStream.Flush();
+
+            StreamReader reader = new StreamReader(tlsStream);
+
+            String line;
+            while ((line = reader.ReadLine()) != null)
+            {
+                Console.WriteLine(">>> " + line);
+            }
+
+            protocol.Close();
+        }
+
+        internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
+        {
+            TcpClient tcp = new TcpClient(hostname, port);
+
+            TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
+            protocol.Connect(client);
+            return protocol;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsProtocolNonBlockingTest.cs b/crypto/test/src/crypto/tls/test/TlsProtocolNonBlockingTest.cs
new file mode 100644
index 000000000..5fe0f32ad
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsProtocolNonBlockingTest.cs
@@ -0,0 +1,126 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class TlsProtocolNonBlockingTest
+    {
+        [Test]
+        public void TestClientServerFragmented()
+        {
+            // tests if it's really non-blocking when partial records arrive
+            DoTestClientServer(true);
+        }
+
+        [Test]
+        public void TestClientServerNonFragmented()
+        {
+            DoTestClientServer(false);
+        }
+
+        private static void DoTestClientServer(bool fragment)
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(secureRandom);
+
+            clientProtocol.Connect(new MockTlsClient(null));
+            serverProtocol.Accept(new MockTlsServer());
+
+            // pump handshake
+            bool hadDataFromServer = true;
+            bool hadDataFromClient = true;
+            while (hadDataFromServer || hadDataFromClient)
+            {
+                hadDataFromServer = PumpData(serverProtocol, clientProtocol, fragment);
+                hadDataFromClient = PumpData(clientProtocol, serverProtocol, fragment);
+            }
+
+            // send data in both directions
+            byte[] data = new byte[1024];
+            secureRandom.NextBytes(data);
+            WriteAndRead(clientProtocol, serverProtocol, data, fragment);
+            WriteAndRead(serverProtocol, clientProtocol, data, fragment);
+
+            // close the connection
+            clientProtocol.Close();
+            PumpData(clientProtocol, serverProtocol, fragment);
+            CheckClosed(serverProtocol);
+            CheckClosed(clientProtocol);
+        }
+
+        private static void WriteAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, bool fragment)
+        {
+            int dataSize = data.Length;
+            writer.OfferOutput(data, 0, dataSize);
+            PumpData(writer, reader, fragment);
+
+            Assert.AreEqual(dataSize, reader.GetAvailableInputBytes());
+            byte[] readData = new byte[dataSize];
+            reader.ReadInput(readData, 0, dataSize);
+            AssertArrayEquals(data, readData);
+        }
+
+        private static bool PumpData(TlsProtocol from, TlsProtocol to, bool fragment)
+        {
+            int byteCount = from.GetAvailableOutputBytes();
+            if (byteCount == 0)
+            {
+                return false;
+            }
+
+            if (fragment)
+            {
+                while (from.GetAvailableOutputBytes() > 0)
+                {
+                    byte[] buffer = new byte[1];
+                    from.ReadOutput(buffer, 0, 1);
+                    to.OfferInput(buffer);
+                }
+            }
+            else
+            {
+                byte[] buffer = new byte[byteCount];
+                from.ReadOutput(buffer, 0, buffer.Length);
+                to.OfferInput(buffer);
+            }
+
+            return true;
+        }
+
+        private static void CheckClosed(TlsProtocol protocol)
+        {
+            Assert.IsTrue(protocol.IsClosed);
+
+            try
+            {
+                protocol.OfferInput(new byte[10]);
+                Assert.Fail("Input was accepted after close");
+            }
+            catch (IOException e)
+            {
+            }
+
+            try
+            {
+                protocol.OfferOutput(new byte[10], 0, 10);
+                Assert.Fail("Output was accepted after close");
+            }
+            catch (IOException e)
+            {
+            }
+        }
+
+        private static void AssertArrayEquals(byte[] a, byte[] b)
+        {
+            Assert.IsTrue(Arrays.AreEqual(a, b));
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsProtocolTest.cs b/crypto/test/src/crypto/tls/test/TlsProtocolTest.cs
new file mode 100644
index 000000000..ba5b90c75
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsProtocolTest.cs
@@ -0,0 +1,80 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class TlsProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe, secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe, secureRandom);
+
+            Server server = new Server(serverProtocol);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            MockTlsClient client = new MockTlsClient(null);
+            clientProtocol.Connect(client);
+
+            // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+            int length = 1000;
+
+            byte[] data = new byte[length];
+            secureRandom.NextBytes(data);
+
+            Stream output = clientProtocol.Stream;
+            output.Write(data, 0, data.Length);
+
+            byte[] echo = new byte[data.Length];
+            int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+            Assert.AreEqual(count, data.Length);
+            Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+            output.Close();
+
+            serverThread.Join();
+        }
+
+        internal class Server
+        {
+            private readonly TlsServerProtocol mServerProtocol;
+
+            internal Server(TlsServerProtocol serverProtocol)
+            {
+                this.mServerProtocol = serverProtocol;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockTlsServer server = new MockTlsServer();
+                    mServerProtocol.Accept(server);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception)
+                {
+                    //throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs b/crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs
new file mode 100644
index 000000000..b059bb2cb
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs
@@ -0,0 +1,80 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class TlsPskProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe, secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe, secureRandom);
+
+            Server server = new Server(serverProtocol);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            MockPskTlsClient client = new MockPskTlsClient(null);
+            clientProtocol.Connect(client);
+
+            // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+            int length = 1000;
+
+            byte[] data = new byte[length];
+            secureRandom.NextBytes(data);
+
+            Stream output = clientProtocol.Stream;
+            output.Write(data, 0, data.Length);
+
+            byte[] echo = new byte[data.Length];
+            int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+            Assert.AreEqual(count, data.Length);
+            Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+            output.Close();
+
+            serverThread.Join();
+        }
+
+        internal class Server
+        {
+            private readonly TlsServerProtocol mServerProtocol;
+
+            internal Server(TlsServerProtocol serverProtocol)
+            {
+                this.mServerProtocol = serverProtocol;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockPskTlsServer server = new MockPskTlsServer();
+                    mServerProtocol.Accept(server);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception)
+                {
+                    //throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsServerTest.cs b/crypto/test/src/crypto/tls/test/TlsServerTest.cs
new file mode 100644
index 000000000..77adf22f7
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsServerTest.cs
@@ -0,0 +1,78 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    /**
+     * A simple test designed to conduct a TLS handshake with an external TLS client.
+     * <p/>
+     * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+     * this package (under 'src/test/resources') for help configuring an external TLS client.
+     */
+    public class TlsServerTest
+    {
+        private static readonly SecureRandom secureRandom = new SecureRandom();
+
+        public static void Main(string[] args)
+        {
+            int port = 5556;
+
+            TcpListener ss = new TcpListener(IPAddress.Any, port);
+            ss.Start();
+            Stream stdout = Console.OpenStandardOutput();
+            while (true)
+            {
+                TcpClient s = ss.AcceptTcpClient();
+                Console.WriteLine("--------------------------------------------------------------------------------");
+                Console.WriteLine("Accepted " + s);
+                ServerThread st = new ServerThread(s, stdout);
+                Thread t = new Thread(new ThreadStart(st.Run));
+                t.Start();
+            }
+        }
+
+        internal class ServerThread
+        {
+            private readonly TcpClient s;
+            private readonly Stream stdout;
+
+            internal ServerThread(TcpClient s, Stream stdout)
+            {
+                this.s = s;
+                this.stdout = stdout;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockTlsServer server = new MockTlsServer();
+                    TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream(), secureRandom);
+                    serverProtocol.Accept(server);
+                    Stream log = new TeeOutputStream(serverProtocol.Stream, stdout);
+                    Streams.PipeAll(serverProtocol.Stream, log);
+                    serverProtocol.Close();
+                }
+                finally
+                {
+                    try
+                    {
+                        s.Close();
+                    }
+                    catch (IOException)
+                    {
+                    }
+                    finally
+                    {
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs b/crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs
new file mode 100644
index 000000000..32e126ff2
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs
@@ -0,0 +1,80 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class TlsSrpProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe, secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe, secureRandom);
+
+            Server server = new Server(serverProtocol);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            MockSrpTlsClient client = new MockSrpTlsClient(null, MockSrpTlsServer.TEST_IDENTITY, MockSrpTlsServer.TEST_PASSWORD);
+            clientProtocol.Connect(client);
+
+            // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+            int length = 1000;
+
+            byte[] data = new byte[length];
+            secureRandom.NextBytes(data);
+
+            Stream output = clientProtocol.Stream;
+            output.Write(data, 0, data.Length);
+
+            byte[] echo = new byte[data.Length];
+            int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+            Assert.AreEqual(count, data.Length);
+            Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+            output.Close();
+
+            serverThread.Join();
+        }
+
+        internal class Server
+        {
+            private readonly TlsServerProtocol mServerProtocol;
+
+            internal Server(TlsServerProtocol serverProtocol)
+            {
+                this.mServerProtocol = serverProtocol;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockSrpTlsServer server = new MockSrpTlsServer();
+                    mServerProtocol.Accept(server);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception)
+                {
+                    //throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestCase.cs b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
new file mode 100644
index 000000000..4b0c12710
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
@@ -0,0 +1,164 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class TlsTestCase
+    {
+        private static void CheckTlsVersion(ProtocolVersion version)
+        {
+            if (version != null && !version.IsTls)
+                throw new InvalidOperationException("Non-TLS version");
+        }
+
+        [Test, TestCaseSource(typeof(TlsTestSuite), "Suite")]
+        public void RunTest(TlsTestConfig config)
+        {
+            CheckTlsVersion(config.clientMinimumVersion);
+            CheckTlsVersion(config.clientOfferVersion);
+            CheckTlsVersion(config.serverMaximumVersion);
+            CheckTlsVersion(config.serverMinimumVersion);
+
+            SecureRandom secureRandom = new SecureRandom();
+
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            NetworkStream clientNet = new NetworkStream(clientPipe);
+            NetworkStream serverNet = new NetworkStream(serverPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientNet, secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverNet, secureRandom);
+
+            TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+            TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+            Server server = new Server(this, serverProtocol, serverImpl);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            Exception caught = null;
+            try
+            {
+                clientProtocol.Connect(clientImpl);
+
+                // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+                int length = 1000;
+
+                byte[] data = new byte[length];
+                secureRandom.NextBytes(data);
+    
+                Stream output = clientProtocol.Stream;
+                output.Write(data, 0, data.Length);
+
+                byte[] echo = new byte[data.Length];
+                int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+                Assert.AreEqual(count, data.Length);
+                Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+                output.Close();
+            }
+            catch (Exception e)
+            {
+                caught = e;
+                LogException(caught);
+            }
+
+            server.AllowExit();
+            serverThread.Join();
+
+            Assert.IsTrue(clientNet.IsClosed, "Client Stream not closed");
+            Assert.IsTrue(serverNet.IsClosed, "Server Stream not closed");
+
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd, "Client fatal alert connection end");
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd, "Server fatal alert connection end");
+
+            Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription, "Client fatal alert description");
+            Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription, "Server fatal alert description");
+
+            if (config.expectFatalAlertConnectionEnd == -1)
+            {
+                Assert.IsNull(caught, "Unexpected client exception");
+                Assert.IsNull(server.mCaught, "Unexpected server exception");
+            }
+        }
+
+        protected virtual void LogException(Exception e)
+        {
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.Error.WriteLine(e);
+            }
+        }
+
+        internal class Server
+        {
+            protected readonly TlsTestCase mOuter;
+            protected readonly TlsServerProtocol mServerProtocol;
+            protected readonly TlsTestServerImpl mServerImpl;
+
+            internal bool mCanExit = false;
+            internal Exception mCaught = null;
+
+            internal Server(TlsTestCase outer, TlsServerProtocol serverProtocol, TlsTestServerImpl serverImpl)
+            {
+                this.mOuter = outer;
+                this.mServerProtocol = serverProtocol;
+                this.mServerImpl = serverImpl;
+            }
+
+            internal void AllowExit()
+            {
+                lock (this)
+                {
+                    mCanExit = true;
+                    Monitor.PulseAll(this);
+                }
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    mServerProtocol.Accept(mServerImpl);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception e)
+                {
+                    mCaught = e;
+                    mOuter.LogException(mCaught);
+                }
+
+                WaitExit();
+            }
+
+            protected void WaitExit()
+            {
+                lock (this)
+                {
+                    while (!mCanExit)
+                    {
+                        try
+                        {
+                            Monitor.Wait(this);
+                        }
+                        catch (ThreadInterruptedException)
+                        {
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
new file mode 100644
index 000000000..48af9e0f8
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
@@ -0,0 +1,262 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class TlsTestClientImpl
+        :   DefaultTlsClient
+    {
+        protected readonly TlsTestConfig mConfig;
+
+        protected int firstFatalAlertConnectionEnd = -1;
+        protected int firstFatalAlertDescription = -1;
+
+        internal TlsTestClientImpl(TlsTestConfig config)
+        {
+            this.mConfig = config;
+        }
+
+        internal int FirstFatalAlertConnectionEnd
+        {
+            get { return firstFatalAlertConnectionEnd; }
+        }
+
+        internal int FirstFatalAlertDescription
+        {
+            get { return firstFatalAlertDescription; }
+        }
+
+        public override ProtocolVersion ClientVersion
+        {
+	        get 
+	        { 
+                if (mConfig.clientOfferVersion != null)
+                {
+                    return mConfig.clientOfferVersion;
+                }
+
+                return base.ClientVersion;
+            }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+	        get 
+	        { 
+                if (mConfig.clientMinimumVersion != null)
+                {
+                    return mConfig.clientMinimumVersion;
+                }
+
+                return base.MinimumVersion;
+	        }
+        }
+
+        public override bool IsFallback
+        {
+            get { return mConfig.clientFallback; }
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.client;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS client raised alert: " + AlertLevel.GetText(alertLevel)
+                    + ", " + AlertDescription.GetText(alertDescription));
+                if (message != null)
+                {
+                    output.WriteLine("> " + message);
+                }
+                if (cause != null)
+                {
+                    output.WriteLine(cause);
+                }
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.server;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel)
+                    + ", " + AlertDescription.GetText(alertDescription));
+            }
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.WriteLine("TLS client negotiated " + serverVersion);
+            }
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(this, mContext);
+        }
+
+        protected virtual Certificate CorruptCertificate(Certificate cert)
+        {
+            X509CertificateStructure[] certList = cert.GetCertificateList();
+            certList[0] = CorruptCertificateSignature(certList[0]);
+            return new Certificate(certList);
+        }
+
+        protected virtual X509CertificateStructure CorruptCertificateSignature(X509CertificateStructure cert)
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector();
+            v.Add(cert.TbsCertificate);
+            v.Add(cert.SignatureAlgorithm);
+            v.Add(CorruptBitString(cert.Signature));
+
+            return X509CertificateStructure.GetInstance(new DerSequence(v));
+        }
+
+        protected virtual DerBitString CorruptBitString(DerBitString bs)
+        {
+            return new DerBitString(CorruptBit(bs.GetBytes()));
+        }
+
+        protected virtual byte[] CorruptBit(byte[] bs)
+        {
+            bs = Arrays.Clone(bs);
+
+            // Flip a random bit
+            int bit = mContext.SecureRandom.Next(bs.Length << 3); 
+            bs[bit >> 3] ^= (byte)(1 << (bit & 7));
+
+            return bs;
+        }
+
+        internal class MyTlsAuthentication
+            :   TlsAuthentication
+        {
+            private readonly TlsTestClientImpl mOuter;
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsTestClientImpl outer, TlsContext context)
+            {
+                this.mOuter = outer;
+                this.mContext = context;
+            }
+
+            public virtual void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                bool isEmpty = serverCertificate == null || serverCertificate.IsEmpty;
+
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+
+                // TODO Cache test resources?
+                if (isEmpty || !(chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-server.pem"))
+                    || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-server-dsa.pem"))
+                    || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-server-ecdsa.pem"))))
+                {
+                    throw new TlsFatalAlert(AlertDescription.bad_certificate);
+                }
+
+                if (TlsTestConfig.DEBUG)
+                {
+                    Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+                    for (int i = 0; i != chain.Length; i++)
+                    {
+                        X509CertificateStructure entry = chain[i];
+                        // TODO Create fingerprint based on certificate signature algorithm digest
+                        Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                            + entry.Subject + ")");
+                    }
+                }
+            }
+
+            public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                if (mOuter.mConfig.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+                    throw new InvalidOperationException();
+                if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)
+                    return null;
+
+                byte[] certificateTypes = certificateRequest.CertificateTypes;
+                if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+                {
+                    return null;
+                }
+
+                TlsSignerCredentials signerCredentials = TlsTestUtilities.LoadSignerCredentials(mContext,
+                    certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                    "x509-client.pem", "x509-client-key.pem");
+
+                if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
+                {
+                    return signerCredentials;
+                }
+
+                return new MyTlsSignerCredentials(mOuter, signerCredentials);
+            }
+        };
+
+        internal class MyTlsSignerCredentials
+            :   TlsSignerCredentials
+        {
+            private readonly TlsTestClientImpl mOuter;
+            private readonly TlsSignerCredentials mInner;
+
+            internal MyTlsSignerCredentials(TlsTestClientImpl outer, TlsSignerCredentials inner)
+            {
+                this.mOuter = outer;
+                this.mInner = inner;
+            }
+
+            public virtual byte[] GenerateCertificateSignature(byte[] hash)
+            {
+                byte[] sig = mInner.GenerateCertificateSignature(hash);
+
+                if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY)
+                {
+                    sig = mOuter.CorruptBit(sig);
+                }
+
+                return sig;
+            }
+
+            public virtual Certificate Certificate
+            {
+                get
+                {
+                    Certificate cert = mInner.Certificate;
+
+                    if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT)
+                    {
+                        cert = mOuter.CorruptCertificate(cert);
+                    }
+
+                    return cert;
+                }
+            }
+
+            public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+            {
+                get { return mInner.SignatureAndHashAlgorithm; }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestConfig.cs b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
new file mode 100644
index 000000000..0d1e7badb
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
@@ -0,0 +1,101 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class TlsTestConfig
+    {
+        public static readonly bool DEBUG = false;
+
+        /**
+         * Client does not authenticate, ignores any certificate request
+         */
+        public const int CLIENT_AUTH_NONE = 0;
+
+        /**
+         * Client will authenticate if it receives a certificate request
+         */
+        public const int CLIENT_AUTH_VALID = 1;
+
+        /**
+         * Client will authenticate if it receives a certificate request, with an invalid certificate
+         */
+        public const int CLIENT_AUTH_INVALID_CERT = 2;
+
+        /**
+         * Client will authenticate if it receives a certificate request, with an invalid CertificateVerify signature
+         */
+        public const int CLIENT_AUTH_INVALID_VERIFY = 3;
+
+        /**
+         * Server will not request a client certificate
+         */
+        public const int SERVER_CERT_REQ_NONE = 0;
+
+        /**
+         * Server will request a client certificate but receiving one is optional
+         */
+        public const int SERVER_CERT_REQ_OPTIONAL = 1;
+
+        /**
+         * Server will request a client certificate and receiving one is mandatory
+         */
+        public const int SERVER_CERT_REQ_MANDATORY = 2;
+
+        /**
+         * Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants.
+         */
+        public int clientAuth = CLIENT_AUTH_VALID;
+
+        /**
+         * Configures the minimum protocol version the client will accept. If null, uses the library's default.
+         */
+        public ProtocolVersion clientMinimumVersion = null;
+
+        /**
+         * Configures the protocol version the client will offer. If null, uses the library's default.
+         */
+        public ProtocolVersion clientOfferVersion = null;
+
+        /**
+         * Configures whether the client will indicate version fallback via TLS_FALLBACK_SCSV.
+         */
+        public bool clientFallback = false;
+
+        /**
+         * Configures whether the test server will send a certificate request.
+         */
+        public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
+
+        /**
+         * Configures the maximum protocol version the server will accept. If null, uses the library's default.
+         */
+        public ProtocolVersion serverMaximumVersion = null;
+
+        /**
+         * Configures the minimum protocol version the server will accept. If null, uses the library's default.
+         */
+        public ProtocolVersion serverMinimumVersion = null;
+
+        /**
+         * Configures the connection end that a fatal alert is expected to be raised. Use ConnectionEnd.* constants.
+         */
+        public int expectFatalAlertConnectionEnd = -1;
+
+        /**
+         * Configures the type of fatal alert expected to be raised. Use AlertDescription.* constants.
+         */
+        public int expectFatalAlertDescription = -1;
+
+        public void ExpectClientFatalAlert(byte alertDescription)
+        {
+            this.expectFatalAlertConnectionEnd = ConnectionEnd.client;
+            this.expectFatalAlertDescription = alertDescription;
+        }
+
+        public void ExpectServerFatalAlert(byte alertDescription)
+        {
+            this.expectFatalAlertConnectionEnd = ConnectionEnd.server;
+            this.expectFatalAlertDescription = alertDescription;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
new file mode 100644
index 000000000..152d5dbdc
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class TlsTestServerImpl
+        :   DefaultTlsServer
+    {
+        protected readonly TlsTestConfig mConfig;
+
+        protected int firstFatalAlertConnectionEnd = -1;
+        protected int firstFatalAlertDescription = -1;
+
+        internal TlsTestServerImpl(TlsTestConfig config)
+        {
+            this.mConfig = config;
+        }
+
+        internal int FirstFatalAlertConnectionEnd
+        {
+            get { return firstFatalAlertConnectionEnd; }
+        }
+
+        internal int FirstFatalAlertDescription
+        {
+            get { return firstFatalAlertDescription; }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+	        get 
+	        { 
+                if (mConfig.serverMaximumVersion != null)
+                {
+                    return mConfig.serverMaximumVersion;
+                }
+
+                return base.MaximumVersion;
+	        }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+	        get 
+	        { 
+                if (mConfig.serverMinimumVersion != null)
+                {
+                    return mConfig.serverMinimumVersion;
+                }
+
+                return base.MinimumVersion;
+	        }
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.server;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS server raised alert: " + AlertLevel.GetText(alertLevel)
+                    + ", " + AlertDescription.GetText(alertDescription));
+                if (message != null)
+                {
+                    output.WriteLine("> " + message);
+                }
+                if (cause != null)
+                {
+                    output.WriteLine(cause);
+                }
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.client;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel)
+                    + ", " + AlertDescription.GetText(alertDescription));
+            }
+        }
+
+        public override ProtocolVersion GetServerVersion()
+        {
+            ProtocolVersion serverVersion = base.GetServerVersion();
+
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.WriteLine("TLS server negotiated " + serverVersion);
+            }
+
+            return serverVersion;
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            if (mConfig.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+            {
+                return null;
+            }
+
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+            IList serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+            }
+
+            IList certificateAuthorities = new ArrayList();
+            certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
+
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+            if (isEmpty != (mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE))
+            {
+                throw new InvalidOperationException();
+            }
+            if (isEmpty && (mConfig.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY))
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+
+            X509CertificateStructure[] chain = clientCertificate.GetCertificateList();
+
+            // TODO Cache test resources?
+            if (!isEmpty && !(chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-client.pem"))
+                || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-client-dsa.pem"))
+                || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-client-ecdsa.pem"))))
+            {
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.WriteLine("TLS server received client certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+        }
+
+        protected override TlsSignerCredentials GetDsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.dsa,
+                "x509-server-dsa.pem", "x509-server-key-dsa.pem");
+        }
+
+        protected override TlsSignerCredentials GetECDsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.ecdsa,
+                "x509-server-ecdsa.pem", "x509-server-key-ecdsa.pem");
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{ "x509-server.pem", "x509-ca.pem" },
+                "x509-server-key.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestSuite.cs b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
new file mode 100644
index 000000000..dfd09d06e
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class TlsTestSuite
+    {
+        // Make the access to constants less verbose 
+        internal class C : TlsTestConfig {}
+
+        public TlsTestSuite()
+        {
+        }
+
+        public static IEnumerable Suite()
+        {
+            IList testSuite = new ArrayList();
+
+            AddFallbackTests(testSuite);
+            AddVersionTests(testSuite, ProtocolVersion.TLSv10);
+            AddVersionTests(testSuite, ProtocolVersion.TLSv11);
+            AddVersionTests(testSuite, ProtocolVersion.TLSv12);
+
+            return testSuite;
+        }
+
+        private static void AddFallbackTests(IList testSuite)
+        {
+            {
+                TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
+                c.clientFallback = true;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackGood"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
+                c.clientOfferVersion = ProtocolVersion.TLSv11;
+                c.clientFallback = true;
+                c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
+                c.clientOfferVersion = ProtocolVersion.TLSv11;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackNone"));
+            }
+        }
+
+        private static void AddVersionTests(IList testSuite, ProtocolVersion version)
+        {
+            string prefix = version.ToString()
+                .Replace(" ", "")
+                .Replace("\\", "")
+                .Replace(".", "")
+                + "_";
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+                c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+                c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+                c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+                c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+            }
+        }
+
+        private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion version)
+        {
+            TlsTestConfig c = new TlsTestConfig();
+            c.clientMinimumVersion = ProtocolVersion.TLSv10;
+            c.clientOfferVersion = ProtocolVersion.TLSv12;
+            c.serverMaximumVersion = version;
+            c.serverMinimumVersion = ProtocolVersion.TLSv10;
+            return c;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
new file mode 100644
index 000000000..a76858ce6
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public abstract class TlsTestUtilities
+    {
+        internal static readonly byte[] RsaCertData = Base64
+            .Decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+                + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+                + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA2MDIwNVoXDTEzMDIyNT"
+                + "A2MDM0NVowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+                + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+                + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+                + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCBSAwEgYDVR"
+                + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAHU55Ncz"
+                + "eglREcTg54YLUlGWu2WOYWhit/iM1eeq8Kivro7q98eW52jTuMI3CI5ulqd0hYzshQKQaZ5GDzErMyM=");
+
+        internal static readonly byte[] DudRsaCertData = Base64
+            .Decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+                + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+                + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA1NDcyOFoXDTEzMDIyNT"
+                + "A1NDkwOFowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+                + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+                + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+                + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCAAEwEgYDVR"
+                + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS"
+                + "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0=");
+
+        internal static string Fingerprint(X509CertificateStructure c)
+        {
+            byte[] der = c.GetEncoded();
+            byte[] sha1 = Sha256DigestOf(der);
+            byte[] hexBytes = Hex.Encode(sha1);
+            string hex = Encoding.ASCII.GetString(hexBytes).ToUpper(CultureInfo.InvariantCulture);
+
+            StringBuilder fp = new StringBuilder();
+            int i = 0;
+            fp.Append(hex.Substring(i, 2));
+            while ((i += 2) < hex.Length)
+            {
+                fp.Append(':');
+                fp.Append(hex.Substring(i, 2));
+            }
+            return fp.ToString();
+        }
+
+        internal static byte[] Sha256DigestOf(byte[] input)
+        {
+            return DigestUtilities.CalculateDigest("SHA256", input);
+        }
+
+        internal static TlsAgreementCredentials LoadAgreementCredentials(TlsContext context,
+            string[] certResources, string keyResource)
+        {
+            Certificate certificate = LoadCertificateChain(certResources);
+            AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+            return new DefaultTlsAgreementCredentials(certificate, privateKey);
+        }
+
+        internal static TlsEncryptionCredentials LoadEncryptionCredentials(TlsContext context,
+            string[] certResources, string keyResource)
+        {
+            Certificate certificate = LoadCertificateChain(certResources);
+            AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+            return new DefaultTlsEncryptionCredentials(context, certificate, privateKey);
+        }
+
+        internal static TlsSignerCredentials LoadSignerCredentials(TlsContext context, string[] certResources,
+            string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+        {
+            Certificate certificate = LoadCertificateChain(certResources);
+            AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+            return new DefaultTlsSignerCredentials(context, certificate, privateKey, signatureAndHashAlgorithm);
+        }
+
+        internal static TlsSignerCredentials LoadSignerCredentials(TlsContext context, IList supportedSignatureAlgorithms,
+            byte signatureAlgorithm, string certResource, string keyResource)
+        {
+            /*
+             * TODO Note that this code fails to provide default value for the client supported
+             * algorithms if it wasn't sent.
+             */
+
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+            if (supportedSignatureAlgorithms != null)
+            {
+                foreach (SignatureAndHashAlgorithm alg in supportedSignatureAlgorithms)
+                {
+                    if (alg.Signature == signatureAlgorithm)
+                    {
+                        signatureAndHashAlgorithm = alg;
+                        break;
+                    }
+                }
+
+                if (signatureAndHashAlgorithm == null)
+                    return null;
+            }
+
+            return LoadSignerCredentials(context, new String[]{ certResource, "x509-ca.pem" },
+                keyResource, signatureAndHashAlgorithm);
+        }
+
+        internal static Certificate LoadCertificateChain(string[] resources)
+        {
+            X509CertificateStructure[] chain = new X509CertificateStructure[resources.Length];
+            for (int i = 0; i < resources.Length; ++i)
+            {
+                chain[i] = LoadCertificateResource(resources[i]);
+            }
+            return new Certificate(chain);
+        }
+
+        internal static X509CertificateStructure LoadCertificateResource(string resource)
+        {
+            PemObject pem = LoadPemResource(resource);
+            if (pem.Type.EndsWith("CERTIFICATE"))
+            {
+                return X509CertificateStructure.GetInstance(pem.Content);
+            }
+            throw new ArgumentException("doesn't specify a valid certificate", "resource");
+        }
+
+        internal static AsymmetricKeyParameter LoadPrivateKeyResource(string resource)
+        {
+            PemObject pem = LoadPemResource(resource);
+            if (pem.Type.EndsWith("RSA PRIVATE KEY"))
+            {
+                RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(pem.Content);
+                return new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent,
+                    rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
+                    rsa.Exponent2, rsa.Coefficient);
+            }
+            if (pem.Type.EndsWith("PRIVATE KEY"))
+            {
+                return PrivateKeyFactory.CreateKey(pem.Content);
+            }
+            throw new ArgumentException("doesn't specify a valid private key", "resource");
+        }
+
+        internal static PemObject LoadPemResource(string resource)
+        {
+            Stream s = SimpleTest.GetTestDataAsStream("tls." + resource);
+            PemReader p = new PemReader(new StreamReader(s));
+            PemObject o = p.ReadPemObject();
+            p.Reader.Close();
+            return o;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs
new file mode 100644
index 000000000..b771ab7cf
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs
@@ -0,0 +1,84 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class UnreliableDatagramTransport
+        :   DatagramTransport
+    {
+        private readonly DatagramTransport transport;
+        private readonly Random random;
+        private readonly int percentPacketLossReceiving, percentPacketLossSending;
+
+        public UnreliableDatagramTransport(DatagramTransport transport, Random random,
+            int percentPacketLossReceiving, int percentPacketLossSending)
+        {
+            if (percentPacketLossReceiving < 0 || percentPacketLossReceiving > 100)
+                throw new ArgumentException("out of range", "percentPacketLossReceiving");
+            if (percentPacketLossSending < 0 || percentPacketLossSending > 100)
+                throw new ArgumentException("out of range", "percentPacketLossSending");
+
+            this.transport = transport;
+            this.random = random;
+            this.percentPacketLossReceiving = percentPacketLossReceiving;
+            this.percentPacketLossSending = percentPacketLossSending;
+        }
+
+        public virtual int GetReceiveLimit()
+        {
+            return transport.GetReceiveLimit();
+        }
+
+        public virtual int GetSendLimit()
+        {
+            return transport.GetSendLimit();
+        }
+
+        public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+        {
+            long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis;
+            for (;;)
+            {
+                int length = transport.Receive(buf, off, len, waitMillis);
+                if (length < 0 || !LostPacket(percentPacketLossReceiving))
+                {
+                    return length;
+                }
+
+                Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)");
+
+                long now = DateTimeUtilities.CurrentUnixMs();
+                if (now >= endMillis)
+                {
+                    return -1;
+                }
+
+                waitMillis = (int)(endMillis - now);
+            }
+        }
+
+        public virtual void Send(byte[] buf, int off, int len)
+        {
+            if (LostPacket(percentPacketLossSending))
+            {
+                Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)");
+            }
+            else
+            {
+                transport.Send(buf, off, len);
+            }
+        }
+
+        public virtual void Close()
+        {
+            transport.Close();
+        }
+
+        private bool LostPacket(int percentPacketLoss)
+        {
+            return percentPacketLoss > 0 && random.Next(100) < percentPacketLoss;
+        }
+    }
+}
diff --git a/crypto/test/src/math/ec/test/AllTests.cs b/crypto/test/src/math/ec/test/AllTests.cs
index f5d7d2249..3e014ffd2 100644
--- a/crypto/test/src/math/ec/test/AllTests.cs
+++ b/crypto/test/src/math/ec/test/AllTests.cs
@@ -7,21 +7,21 @@ namespace Org.BouncyCastle.Math.EC.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-//            junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-        public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-			TestSuite suite = new TestSuite("EC Math tests");
-
-			suite.Add(new ECPointTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("EC Math tests");
+                suite.Add(new ECAlgorithmsTest());
+                suite.Add(new ECPointTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/math/ec/test/ECAlgorithmsTest.cs b/crypto/test/src/math/ec/test/ECAlgorithmsTest.cs
new file mode 100644
index 000000000..b950c8b4b
--- /dev/null
+++ b/crypto/test/src/math/ec/test/ECAlgorithmsTest.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.EC;
+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.Math.EC.Tests
+{
+    [TestFixture]
+    public class ECAlgorithmsTest
+    {
+        private const int SCALE = 4;
+        private static readonly SecureRandom RND = new SecureRandom();
+
+        [Test]
+        public void TestSumOfMultiplies()
+        {
+            foreach (X9ECParameters x9 in GetTestCurves())
+            {
+                ECPoint[] points = new ECPoint[SCALE];
+                BigInteger[] scalars = new BigInteger[SCALE];
+                for (int i = 0; i < SCALE; ++i)
+                {
+                    points[i] = GetRandomPoint(x9);
+                    scalars[i] = GetRandomScalar(x9);
+                }
+
+                ECPoint u = x9.Curve.Infinity;
+                for (int i = 0; i < SCALE; ++i)
+                {
+                    u = u.Add(points[i].Multiply(scalars[i]));
+
+                    ECPoint v = ECAlgorithms.SumOfMultiplies(CopyPoints(points, i + 1), CopyScalars(scalars, i + 1));
+
+                    ECPoint[] results = new ECPoint[] { u, v };
+                    x9.Curve.NormalizeAll(results);
+
+                    AssertPointsEqual("ECAlgorithms.SumOfMultiplies is incorrect", results[0], results[1]);
+                }
+            }
+        }
+
+        [Test]
+        public void TestSumOfTwoMultiplies()
+        {
+            foreach (X9ECParameters x9 in GetTestCurves())
+            {
+                ECPoint p = GetRandomPoint(x9);
+                BigInteger a = GetRandomScalar(x9);
+
+                for (int i = 0; i < SCALE; ++i)
+                {
+                    ECPoint q = GetRandomPoint(x9);
+                    BigInteger b = GetRandomScalar(x9);
+
+                    ECPoint u = p.Multiply(a).Add(q.Multiply(b));
+                    ECPoint v = ECAlgorithms.ShamirsTrick(p, a, q, b);
+                    ECPoint w = ECAlgorithms.SumOfTwoMultiplies(p, a, q, b);
+
+                    ECPoint[] results = new ECPoint[] { u, v, w };
+                    x9.Curve.NormalizeAll(results);
+
+                    AssertPointsEqual("ECAlgorithms.ShamirsTrick is incorrect", results[0], results[1]);
+                    AssertPointsEqual("ECAlgorithms.SumOfTwoMultiplies is incorrect", results[0], results[2]);
+
+                    p = q;
+                    a = b;
+                }
+            }
+        }
+
+        private void AssertPointsEqual(string message, ECPoint a, ECPoint b)
+        {
+            Assert.AreEqual(a, b, message);
+        }
+
+        private ECPoint[] CopyPoints(ECPoint[] ps, int len)
+        {
+            ECPoint[] result = new ECPoint[len];
+            Array.Copy(ps, 0, result, 0, len);
+            return result;
+        }
+
+        private BigInteger[] CopyScalars(BigInteger[] ks, int len)
+        {
+            BigInteger[] result = new BigInteger[len];
+            Array.Copy(ks, 0, result, 0, len);
+            return result;
+        }
+
+        private ECPoint GetRandomPoint(X9ECParameters x9)
+        {
+            return x9.G.Multiply(GetRandomScalar(x9));
+        }
+
+        private BigInteger GetRandomScalar(X9ECParameters x9)
+        {
+            return new BigInteger(x9.N.BitLength, RND);
+        }
+
+        private IList GetTestCurves()
+        {
+            ArrayList x9s = new ArrayList();
+            ArrayList names = new ArrayList();
+            CollectionUtilities.AddRange(names, ECNamedCurveTable.Names);
+            CollectionUtilities.AddRange(names, CustomNamedCurves.Names);
+            foreach (string name in names)
+            {
+                X9ECParameters x9 = ECNamedCurveTable.GetByName(name);
+                if (x9 != null)
+                {
+                    AddTestCurves(x9s, x9);
+                }
+
+                x9 = CustomNamedCurves.GetByName(name);
+                if (x9 != null)
+                {
+                    AddTestCurves(x9s, x9);
+                }
+            }
+            return x9s;
+        }
+
+        private void AddTestCurves(IList x9s, X9ECParameters x9)
+        {
+            ECCurve curve = x9.Curve;
+
+            int[] coords = ECCurve.GetAllCoordinateSystems();
+            for (int i = 0; i < coords.Length; ++i)
+            {
+                int coord = coords[i];
+                if (curve.CoordinateSystem == coord)
+                {
+                    x9s.Add(x9);
+                }
+                else if (curve.SupportsCoordinateSystem(coord))
+                {
+                    ECCurve c = curve.Configure().SetCoordinateSystem(coord).Create();
+                    x9s.Add(new X9ECParameters(c, c.ImportPoint(x9.G), x9.N, x9.H));
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/math/ec/test/ECPointPerformanceTest.cs b/crypto/test/src/math/ec/test/ECPointPerformanceTest.cs
index 51febaf32..2bcd5b502 100644
--- a/crypto/test/src/math/ec/test/ECPointPerformanceTest.cs
+++ b/crypto/test/src/math/ec/test/ECPointPerformanceTest.cs
@@ -68,7 +68,8 @@ namespace Org.BouncyCastle.Math.EC.Tests
                     ECCurve c = C;
                     ECPoint g = G;
 
-                    if (c.CoordinateSystem != coord)
+                    bool defaultCoord = (c.CoordinateSystem == coord);
+                    if (!defaultCoord)
                     {
                         c = C.Configure().SetCoordinateSystem(coord).Create();
                         g = c.ImportPoint(G);
@@ -77,9 +78,10 @@ namespace Org.BouncyCastle.Math.EC.Tests
                     double avgRate = RandMult(random, g, n);
                     string coordName = COORD_NAMES[coord];
                     StringBuilder sb = new StringBuilder();
-                    sb.Append("  ");
+                    sb.Append("   ");
+                    sb.Append(defaultCoord ? '*' : ' ');
                     sb.Append(coordName);
-                    for (int j = sb.Length; j < 28; ++j)
+                    for (int j = sb.Length; j < 30; ++j)
                     {
                         sb.Append(' ');
                     }
diff --git a/crypto/test/src/math/ec/test/ECPointTest.cs b/crypto/test/src/math/ec/test/ECPointTest.cs
index 4bad914fd..3c10170f7 100644
--- a/crypto/test/src/math/ec/test/ECPointTest.cs
+++ b/crypto/test/src/math/ec/test/ECPointTest.cs
@@ -288,35 +288,6 @@ namespace Org.BouncyCastle.Math.EC.Tests
         }
 
         /**
-         * 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 multiplier.
-         * @return The result of the point multiplication <code>kP</code>.
-         */
-        private ECPoint Multiply(ECPoint p, BigInteger k)
-        {
-            ECPoint q = p.Curve.Infinity;
-            int t = k.BitLength;
-            for (int i = 0; i < t; i++)
-            {
-                if (i != 0)
-                {
-                    p = p.Twice();
-                }
-                if (k.TestBit(i))
-                {
-                    q = q.Add(p);
-                }
-            }
-            return q;
-        }
-
-        /**
          * Checks, if the point multiplication algorithm of the given point yields
          * the same result as point multiplication done by the reference
          * implementation given in <code>multiply()</code>. This method chooses a
@@ -331,7 +302,7 @@ namespace Org.BouncyCastle.Math.EC.Tests
         private void ImplTestMultiply(ECPoint p, int numBits)
         {
             BigInteger k = new BigInteger(numBits, secRand);
-            ECPoint reff = Multiply(p, k);
+            ECPoint reff = ECAlgorithms.ReferenceMultiply(p, k);
             ECPoint q = p.Multiply(k);
             AssertPointsEqual("ECPoint.Multiply is incorrect", reff, q);
         }
@@ -355,7 +326,7 @@ namespace Org.BouncyCastle.Math.EC.Tests
 
             do
             {
-                ECPoint reff = Multiply(p, k);
+                ECPoint reff = ECAlgorithms.ReferenceMultiply(p, k);
                 ECPoint q = p.Multiply(k);
                 AssertPointsEqual("ECPoint.Multiply is incorrect", reff, q);
                 k = k.Add(BigInteger.One);
@@ -512,25 +483,66 @@ namespace Org.BouncyCastle.Math.EC.Tests
             CollectionUtilities.AddRange(names, ECNamedCurveTable.Names);
             CollectionUtilities.AddRange(names, CustomNamedCurves.Names);
 
-            foreach (string name in names)
+            ISet uniqNames = new HashSet(names);
+
+            foreach (string name in uniqNames)
             {
-                X9ECParameters x9ECParameters = ECNamedCurveTable.GetByName(name);
-                if (x9ECParameters != null)
+                X9ECParameters x9A = ECNamedCurveTable.GetByName(name);
+                X9ECParameters x9B = CustomNamedCurves.GetByName(name);
+
+                if (x9A != null && x9B != null)
+                {
+                    Assert.AreEqual(x9A.Curve.Field, x9B.Curve.Field);
+                    Assert.AreEqual(x9A.Curve.A.ToBigInteger(), x9B.Curve.A.ToBigInteger());
+                    Assert.AreEqual(x9A.Curve.B.ToBigInteger(), x9B.Curve.B.ToBigInteger());
+                    AssertOptionalValuesAgree(x9A.Curve.Cofactor, x9B.Curve.Cofactor);
+                    AssertOptionalValuesAgree(x9A.Curve.Order, x9B.Curve.Order);
+
+                    AssertPointsEqual("Custom curve base-point inconsistency", x9A.G, x9B.G);
+
+                    Assert.AreEqual(x9A.H, x9B.H);
+                    Assert.AreEqual(x9A.N, x9B.N);
+                    AssertOptionalValuesAgree(x9A.GetSeed(), x9B.GetSeed());
+
+                    BigInteger k = new BigInteger(x9A.N.BitLength, secRand);
+                    ECPoint pA = x9A.G.Multiply(k);
+                    ECPoint pB = x9B.G.Multiply(k);
+                    AssertPointsEqual("Custom curve multiplication inconsistency", pA, pB);
+                }
+
+                if (x9A != null)
                 {
-                    ImplAddSubtractMultiplyTwiceEncodingTestAllCoords(x9ECParameters);
+                    ImplAddSubtractMultiplyTwiceEncodingTestAllCoords(x9A);
                 }
 
-                x9ECParameters = CustomNamedCurves.GetByName(name);
-                if (x9ECParameters != null)
+                if (x9B != null)
                 {
-                    ImplAddSubtractMultiplyTwiceEncodingTestAllCoords(x9ECParameters);
+                    ImplAddSubtractMultiplyTwiceEncodingTestAllCoords(x9B);
                 }
             }
         }
 
         private void AssertPointsEqual(string message, ECPoint a, ECPoint b)
         {
-			Assert.AreEqual(a, b, message);
+            // NOTE: We intentionally test points for equality in both directions
+            Assert.AreEqual(a, b, message);
+            Assert.AreEqual(b, a, message);
+        }
+
+        private void AssertOptionalValuesAgree(object a, object b)
+        {
+            if (a != null && b != null)
+            {
+                Assert.AreEqual(a, b);
+            }
+        }
+
+        private void AssertOptionalValuesAgree(byte[] a, byte[] b)
+        {
+            if (a != null && b != null)
+            {
+                Assert.IsTrue(Arrays.AreEqual(a, b));
+            }
         }
     }
 }
diff --git a/crypto/test/src/math/ec/test/TnafTest.cs b/crypto/test/src/math/ec/test/TnafTest.cs
index c04ae00d2..f5a58e858 100644
--- a/crypto/test/src/math/ec/test/TnafTest.cs
+++ b/crypto/test/src/math/ec/test/TnafTest.cs
@@ -40,7 +40,7 @@
 //		{
 //			X9ECParameters x9ECParameters = SecNamedCurves.GetByName(curveName);
 //
-//			F2mCurve curve = (F2mCurve)x9ECParameters.Curve;
+//			AbstractF2mCurve curve = (AbstractF2mCurve)x9ECParameters.Curve;
 //			BigInteger n = curve.N;
 //
 //			// The generator is multiplied by random b to get random q
diff --git a/crypto/test/src/math/test/AllTests.cs b/crypto/test/src/math/test/AllTests.cs
index 6f2b50140..40cfe3774 100644
--- a/crypto/test/src/math/test/AllTests.cs
+++ b/crypto/test/src/math/test/AllTests.cs
@@ -7,21 +7,20 @@ namespace Org.BouncyCastle.Math.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-//            junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-        public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-			TestSuite suite = new TestSuite("Math tests");
-
-			suite.Add(new BigIntegerTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("Math tests");
+                suite.Add(new BigIntegerTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/ocsp/test/AllTests.cs b/crypto/test/src/ocsp/test/AllTests.cs
index 2b30e3ad4..5e919fd91 100644
--- a/crypto/test/src/ocsp/test/AllTests.cs
+++ b/crypto/test/src/ocsp/test/AllTests.cs
@@ -9,21 +9,20 @@ namespace Org.BouncyCastle.Ocsp.Tests
 {
 	public class AllTests
 	{
-		public static void Main(
-			string[] args)
-		{
-			//junit.textui.TestRunner.run(suite());
-			EventListener el = new NullListener();
-			suite().Run(el);
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		public static TestSuite suite()
-		{
-			TestSuite suite = new TestSuite("OCSP Tests");
-
-			suite.Add(new OcspTest());
-
-			return suite;
-		}
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("OCSP Tests");
+                suite.Add(new OcspTest());
+                return suite;
+            }
+        }
 	}
 }
diff --git a/crypto/test/src/openpgp/examples/ClearSignedFileProcessor.cs b/crypto/test/src/openpgp/examples/ClearSignedFileProcessor.cs
index 14fa37269..8e4f7a3b5 100644
--- a/crypto/test/src/openpgp/examples/ClearSignedFileProcessor.cs
+++ b/crypto/test/src/openpgp/examples/ClearSignedFileProcessor.cs
@@ -124,6 +124,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
 					outStr.Write(lineSep, 0, lineSep.Length);
 				}
 			}
+            else
+            {
+                // a single line file
+                if (lookAhead != -1)
+                {
+                    byte[] line = lineOut.ToArray();
+                    outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
+                    outStr.Write(lineSep, 0, lineSep.Length);
+                }
+            }
 
 			outStr.Close();
 
@@ -333,19 +343,19 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
         {
             if (args[0].Equals("-s"))
             {
-				Stream fis = File.OpenRead(args[2]);
-				Stream fos = File.Create(args[1] + ".asc");
+                Stream fis = File.OpenRead(args[2]);
+                Stream fos = File.Create(args[1] + ".asc");
 
-				Stream keyIn = PgpUtilities.GetDecoderStream(fis);
+                Stream keyIn = PgpUtilities.GetDecoderStream(fis);
 
-				string digestName = (args.Length == 4)
-					?	"SHA1"
-					:	args[4];
+                string digestName = (args.Length == 4)
+                    ?	"SHA1"
+                    :	args[4];
 
-				SignFile(args[1], keyIn, fos, args[3].ToCharArray(), digestName);
+                SignFile(args[1], keyIn, fos, args[3].ToCharArray(), digestName);
 
-				fis.Close();
-				fos.Close();
+                fis.Close();
+                fos.Close();
             }
             else if (args[0].Equals("-v"))
             {
diff --git a/crypto/test/src/openpgp/examples/PublicKeyRingDump.cs b/crypto/test/src/openpgp/examples/PublicKeyRingDump.cs
index bb6108f2d..cbf553795 100644
--- a/crypto/test/src/openpgp/examples/PublicKeyRingDump.cs
+++ b/crypto/test/src/openpgp/examples/PublicKeyRingDump.cs
@@ -35,8 +35,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
                     return "ElGamalEncrypt";
                 case PublicKeyAlgorithmTag.Dsa:
                     return "DSA";
-                case PublicKeyAlgorithmTag.EC:
-                    return "EC";
+                case PublicKeyAlgorithmTag.ECDH:
+                    return "ECDH";
                 case PublicKeyAlgorithmTag.ECDsa:
                     return "ECDSA";
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
diff --git a/crypto/test/src/openpgp/examples/test/AllTests.cs b/crypto/test/src/openpgp/examples/test/AllTests.cs
index 180d2fa80..c20b599d7 100644
--- a/crypto/test/src/openpgp/examples/test/AllTests.cs
+++ b/crypto/test/src/openpgp/examples/test/AllTests.cs
@@ -4,7 +4,9 @@ using System.IO;
 using NUnit.Core;
 using NUnit.Framework;
 
+using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 {
@@ -159,7 +161,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 		[Test]
 		public void TestRsaKeyGeneration() 
 		{
-			RsaKeyRingGenerator.Main(new string[] { "test", "password" });
+			RsaKeyRingGenerator.Main(new string[]{ "test", "password" });
 
 			CreateSmallTestInput();
 			CreateLargeTestInput();
@@ -168,7 +170,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			CheckKeyBasedEncryption("bpg");
 			CheckLargeKeyBasedEncryption("bpg");
 
-			RsaKeyRingGenerator.Main(new string[] { "-a", "test", "password" });
+			RsaKeyRingGenerator.Main(new string[]{ "-a", "test", "password" });
 
 			CheckSigning("asc");
 			CheckKeyBasedEncryption("asc");
@@ -178,7 +180,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 		[Test]
 		public void TestDsaElGamalKeyGeneration() 
 		{
-			DsaElGamalKeyRingGenerator.Main(new string[] { "test", "password" });
+			DsaElGamalKeyRingGenerator.Main(new string[]{ "test", "password" });
 
 			CreateSmallTestInput();
 			CreateLargeTestInput();
@@ -187,7 +189,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			CheckKeyBasedEncryption("bpg");
 			CheckLargeKeyBasedEncryption("bpg");
 
-			DsaElGamalKeyRingGenerator.Main(new string[] { "-a", "test", "password" });
+			DsaElGamalKeyRingGenerator.Main(new string[]{ "-a", "test", "password" });
 
 			CheckSigning("asc");
 			CheckKeyBasedEncryption("asc");
@@ -200,23 +202,23 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			Console.Error.Flush();
 			_currentErr.SetLength(0);
 
-			PbeFileProcessor.Main(new string[] { "-e", "test.txt", "password" });
+			PbeFileProcessor.Main(new string[]{ "-e", "test.txt", "password" });
 
-			PbeFileProcessor.Main(new string[] { "-d", "test.txt.bpg", "password" });
+			PbeFileProcessor.Main(new string[]{ "-d", "test.txt.bpg", "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("no message integrity check", GetLine(_currentErr));
 
-			PbeFileProcessor.Main(new string[] { "-e", "-i", "test.txt", "password" });
+			PbeFileProcessor.Main(new string[]{ "-e", "-i", "test.txt", "password" });
 
-			PbeFileProcessor.Main(new string[] { "-d", "test.txt.bpg", "password" });
+			PbeFileProcessor.Main(new string[]{ "-d", "test.txt.bpg", "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("message integrity check passed", GetLine(_currentErr));
 
-			PbeFileProcessor.Main(new string[] { "-e", "-ai", "test.txt", "password" });
+			PbeFileProcessor.Main(new string[]{ "-e", "-ai", "test.txt", "password" });
 
-			PbeFileProcessor.Main(new string[] { "-d", "test.txt.asc", "password" });
+			PbeFileProcessor.Main(new string[]{ "-d", "test.txt.asc", "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("message integrity check passed", GetLine(_currentErr));
@@ -232,9 +234,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			CheckClearSignedVerify(crNlSignedMessage);
 			CheckClearSignedVerify(crNlSignedMessageTrailingWhiteSpace);
 
-			ClearSignedFileProcessor.Main(new string[] { "-v", "test.txt.asc", "pub.bpg" });
+			ClearSignedFileProcessor.Main(new string[]{ "-v", "test.txt.asc", "pub.bpg" });
 
-			RsaKeyRingGenerator.Main(new string[] { "test", "password" });
+			RsaKeyRingGenerator.Main(new string[]{ "test", "password" });
 
 			CheckClearSigned(crOnlyMessage);
 			CheckClearSigned(nlOnlyMessage);
@@ -247,24 +249,52 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 		{
 			CreateTestFile(clearSignedPublicKey, "test.txt");
 
-			ClearSignedFileProcessor.Main(new String[] { "-s", "test.txt", "secret.bpg", "password" });
+			ClearSignedFileProcessor.Main(new string[]{ "-s", "test.txt", "secret.bpg", "password" });
 		}
 
-		private void CheckClearSignedVerify(
+        [Test]
+        public void TestClearSignedSingleLine()
+        {
+            CreateTestData("This is a test payload!" + Environment.NewLine, "test.txt");
+            CreateTestData("This is a test payload!" + Environment.NewLine, "test.bak");
+
+            ClearSignedFileProcessor.Main(new string[]{"-s", "test.txt", "secret.bpg", "password"});
+            ClearSignedFileProcessor.Main(new string[]{"-v", "test.txt.asc", "pub.bpg"});
+
+            CompareFile("test.bak", "test.txt");
+        }
+
+        private void CheckClearSignedVerify(
 			string message)
 		{
 			CreateTestData(message, "test.txt.asc");
 
-			ClearSignedFileProcessor.Main(new string[] { "-v", "test.txt.asc", "pub.bpg" });
+			ClearSignedFileProcessor.Main(new string[]{ "-v", "test.txt.asc", "pub.bpg" });
 		}
 
-		private void CheckClearSigned(
+        private void CompareFile(string file1, string file2)
+        {
+            byte[] data1 = GetFileContents(file1);
+            byte[] data2 = GetFileContents(file2);
+
+            Assert.IsTrue(Arrays.AreEqual(data1, data2));
+        }
+
+        private byte[] GetFileContents(string name)
+        {
+            FileStream fs = File.OpenRead(name);
+            byte[] contents = Streams.ReadAll(fs);
+            fs.Close();
+            return contents;
+        }
+
+        private void CheckClearSigned(
 			string message)
 		{
 			CreateTestData(message, "test.txt");
 
-			ClearSignedFileProcessor.Main(new string[] { "-s", "test.txt", "secret.bpg", "password" });
-			ClearSignedFileProcessor.Main(new string[] { "-v", "test.txt.asc", "pub.bpg" });
+			ClearSignedFileProcessor.Main(new string[]{ "-s", "test.txt", "secret.bpg", "password" });
+			ClearSignedFileProcessor.Main(new string[]{ "-v", "test.txt.asc", "pub.bpg" });
 		}
 
 		private void CheckSigning(
@@ -273,14 +303,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			Console.Out.Flush();
 			_currentOut.SetLength(0);
 
-			SignedFileProcessor.Main(new string[] { "-s", "test.txt", "secret." + type, "password" });
-			SignedFileProcessor.Main(new string[] { "-v", "test.txt.bpg", "pub." + type });
+			SignedFileProcessor.Main(new string[]{ "-s", "test.txt", "secret." + type, "password" });
+			SignedFileProcessor.Main(new string[]{ "-v", "test.txt.bpg", "pub." + type });
 
 			Console.Out.Flush();
 			Assert.AreEqual("signature verified.", GetLine(_currentOut));
 
-			SignedFileProcessor.Main(new string[] { "-s", "-a", "test.txt", "secret." + type, "password" });
-			SignedFileProcessor.Main(new string[] { "-v", "test.txt.asc", "pub." + type });
+			SignedFileProcessor.Main(new string[]{ "-s", "-a", "test.txt", "secret." + type, "password" });
+			SignedFileProcessor.Main(new string[]{ "-v", "test.txt.asc", "pub." + type });
 
 			Console.Out.Flush();
 			Assert.AreEqual("signature verified.", GetLine(_currentOut));
@@ -292,20 +322,20 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			Console.Error.Flush();
 			_currentErr.SetLength(0);
 
-			KeyBasedFileProcessor.Main(new string[] { "-e", "test.txt", "pub." + type });
-			KeyBasedFileProcessor.Main(new string[] { "-d", "test.txt.bpg", "secret." + type, "password" });
+			KeyBasedFileProcessor.Main(new string[]{ "-e", "test.txt", "pub." + type });
+			KeyBasedFileProcessor.Main(new string[]{ "-d", "test.txt.bpg", "secret." + type, "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("no message integrity check", GetLine(_currentErr));
 
-			KeyBasedFileProcessor.Main(new string[] { "-e", "-i", "test.txt", "pub." + type });
-			KeyBasedFileProcessor.Main(new string[] { "-d", "test.txt.bpg", "secret." + type, "password" });
+			KeyBasedFileProcessor.Main(new string[]{ "-e", "-i", "test.txt", "pub." + type });
+			KeyBasedFileProcessor.Main(new string[]{ "-d", "test.txt.bpg", "secret." + type, "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("message integrity check passed", GetLine(_currentErr));
 
-			KeyBasedFileProcessor.Main(new string[] { "-e", "-ai", "test.txt", "pub." + type });
-			KeyBasedFileProcessor.Main(new string[] { "-d", "test.txt.asc", "secret." + type, "password" });
+			KeyBasedFileProcessor.Main(new string[]{ "-e", "-ai", "test.txt", "pub." + type });
+			KeyBasedFileProcessor.Main(new string[]{ "-d", "test.txt.asc", "secret." + type, "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("message integrity check passed", GetLine(_currentErr));
@@ -317,20 +347,20 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			Console.Error.Flush();
 			_currentErr.SetLength(0);
 
-			KeyBasedLargeFileProcessor.Main(new string[] { "-e", "large.txt", "pub." + type });
-			KeyBasedLargeFileProcessor.Main(new string[] { "-d", "large.txt.bpg", "secret." + type, "password" });
+			KeyBasedLargeFileProcessor.Main(new string[]{ "-e", "large.txt", "pub." + type });
+			KeyBasedLargeFileProcessor.Main(new string[]{ "-d", "large.txt.bpg", "secret." + type, "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("no message integrity check", GetLine(_currentErr));
 
-			KeyBasedLargeFileProcessor.Main(new string[] { "-e", "-i", "large.txt", "pub." + type });
-			KeyBasedLargeFileProcessor.Main(new string[] { "-d", "large.txt.bpg", "secret." + type, "password" });
+			KeyBasedLargeFileProcessor.Main(new string[]{ "-e", "-i", "large.txt", "pub." + type });
+			KeyBasedLargeFileProcessor.Main(new string[]{ "-d", "large.txt.bpg", "secret." + type, "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("message integrity check passed", GetLine(_currentErr));
 
-			KeyBasedLargeFileProcessor.Main(new string[] { "-e", "-ai", "large.txt", "pub." + type });
-			KeyBasedLargeFileProcessor.Main(new string[] { "-d", "large.txt.asc", "secret." + type, "password" });
+			KeyBasedLargeFileProcessor.Main(new string[]{ "-e", "-ai", "large.txt", "pub." + type });
+			KeyBasedLargeFileProcessor.Main(new string[]{ "-d", "large.txt.asc", "secret." + type, "password" });
 
 			Console.Error.Flush();
 			Assert.AreEqual("message integrity check passed", GetLine(_currentErr));
@@ -359,7 +389,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			string	testData,
 			string	name)
 		{
-			TextWriter bfOut = new StreamWriter(File.Create(name));
+            FileStream fOut = File.Create(name);
+			TextWriter bfOut = new StreamWriter(fOut);
 			bfOut.Write(testData);
 			bfOut.Close();
 		}
@@ -382,22 +413,20 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			return bRd.ReadLine();
 		}
 
-		public static void Main(
-			string[] args)
-		{
-			//junit.textui.TestRunner.run(suite());
-			EventListener el = new NullListener();
-			suite().Run(el);
-		}
-
-		public static TestSuite suite()
-		{
-			TestSuite suite = new TestSuite("OpenPGP Example Tests");
-
-			suite.Add(new AllTests());
-
-			return suite;
-		}
-
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
+
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("OpenPGP Example Tests");
+                suite.Add(new AllTests());
+                return suite;
+            }
+        }
 	}
 }
diff --git a/crypto/test/src/openpgp/test/PGPSignatureTest.cs b/crypto/test/src/openpgp/test/PGPSignatureTest.cs
index 3aba2c302..40fee7a5f 100644
--- a/crypto/test/src/openpgp/test/PGPSignatureTest.cs
+++ b/crypto/test/src/openpgp/test/PGPSignatureTest.cs
@@ -12,750 +12,1034 @@ using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
 {
-	[TestFixture]
-	public class PgpSignatureTest
-		: SimpleTest
-	{
-		private const int[] NO_PREFERENCES = null;
-		private static readonly int[] PREFERRED_SYMMETRIC_ALGORITHMS
-			= new int[] { (int)SymmetricKeyAlgorithmTag.Aes128, (int)SymmetricKeyAlgorithmTag.TripleDes };
-		private static readonly int[] PREFERRED_HASH_ALGORITHMS
-			= new int[] { (int)HashAlgorithmTag.Sha1, (int)HashAlgorithmTag.Sha256 };
-		private static readonly int[] PREFERRED_COMPRESSION_ALGORITHMS
-			= new int[] { (int)CompressionAlgorithmTag.ZLib };
-
-		private const int TEST_EXPIRATION_TIME = 10000;
-		private const string TEST_USER_ID = "test user id";
-		private static readonly byte[] TEST_DATA = Encoding.ASCII.GetBytes("hello world!\nhello world!\n");
-		private static readonly byte[] TEST_DATA_WITH_CRLF = Encoding.ASCII.GetBytes("hello world!\r\nhello world!\r\n");
-
-		private static readonly byte[] dsaKeyRing = Base64.Decode(
-			"lQHhBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ"
-			+ "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV"
-			+ "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/"
-			+ "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug"
-			+ "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu"
-			+ "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ"
-			+ "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz"
-			+ "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej"
-			+ "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbP4DAwIDIBTxWjkC"
-			+ "GGAWQO2jy9CTvLHJEoTO7moHrp1FxOVpQ8iJHyRqZzLllO26OzgohbiPYz8u9qCu"
-			+ "lZ9Xn7QzRXJpYyBFY2hpZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNh"
-			+ "c3RsZS5vcmc+iFkEExECABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j"
-			+ "9enEyjRDAlwAnjTjjt57NKIgyym7OTCwzIU3xgFpAJ0VO5m5PfQKmGJRhaewLSZD"
-			+ "4nXkHg==");
-
-		private static readonly char[] dsaPass = "hello world".ToCharArray();
-
-		private static readonly byte[] rsaKeyRing = Base64.Decode(
-			  "lQIEBEBXUNMBBADScQczBibewnbCzCswc/9ut8R0fwlltBRxMW0NMdKJY2LF"
-			+ "7k2COeLOCIU95loJGV6ulbpDCXEO2Jyq8/qGw1qD3SCZNXxKs3GS8Iyh9Uwd"
-			+ "VL07nMMYl5NiQRsFB7wOb86+94tYWgvikVA5BRP5y3+O3GItnXnpWSJyREUy"
-			+ "6WI2QQAGKf4JAwIVmnRs4jtTX2DD05zy2mepEQ8bsqVAKIx7lEwvMVNcvg4Y"
-			+ "8vFLh9Mf/uNciwL4Se/ehfKQ/AT0JmBZduYMqRU2zhiBmxj4cXUQ0s36ysj7"
-			+ "fyDngGocDnM3cwPxaTF1ZRBQHSLewP7dqE7M73usFSz8vwD/0xNOHFRLKbsO"
-			+ "RqDlLA1Cg2Yd0wWPS0o7+qqk9ndqrjjSwMM8ftnzFGjShAdg4Ca7fFkcNePP"
-			+ "/rrwIH472FuRb7RbWzwXA4+4ZBdl8D4An0dwtfvAO+jCZSrLjmSpxEOveJxY"
-			+ "GduyR4IA4lemvAG51YHTHd4NXheuEqsIkn1yarwaaj47lFPnxNOElOREMdZb"
-			+ "nkWQb1jfgqO24imEZgrLMkK9bJfoDnlF4k6r6hZOp5FSFvc5kJB4cVo1QJl4"
-			+ "pwCSdoU6luwCggrlZhDnkGCSuQUUW45NE7Br22NGqn4/gHs0KCsWbAezApGj"
-			+ "qYUCfX1bcpPzUMzUlBaD5rz2vPeO58CDtBJ0ZXN0ZXIgPHRlc3RAdGVzdD6I"
-			+ "sgQTAQIAHAUCQFdQ0wIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQs8JyyQfH"
-			+ "97I1QgP8Cd+35maM2cbWV9iVRO+c5456KDi3oIUSNdPf1NQrCAtJqEUhmMSt"
-			+ "QbdiaFEkPrORISI/2htXruYn0aIpkCfbUheHOu0sef7s6pHmI2kOQPzR+C/j"
-			+ "8D9QvWsPOOso81KU2axUY8zIer64Uzqc4szMIlLw06c8vea27RfgjBpSCryw"
-			+ "AgAA");
-
-		private static readonly char[] rsaPass = "2002 Buffalo Sabres".ToCharArray();
-
-		private static readonly byte[] nullPacketsSubKeyBinding = Base64.Decode(
-			"iDYEGBECAAAAACp9AJ9PlJCrFpi+INwG7z61eku2Wg1HaQCgl33X5Egj+Kf7F9CXIWj2iFCvQDo=");
-
-		public override void PerformTest()
-		{
-			//
-			// RSA tests
-			//
-			PgpSecretKeyRing pgpPriv = new PgpSecretKeyRing(rsaKeyRing);
-			PgpSecretKey secretKey = pgpPriv.GetSecretKey();
-			PgpPrivateKey pgpPrivKey = secretKey.ExtractPrivateKey(rsaPass);
-
-			try
-			{
-				doTestSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-
-				Fail("RSA wrong key test failed.");
-			}
-			catch (PgpException)
-			{
-				// expected
-			}
-
-			try
-			{
-				doTestSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-
-				Fail("RSA V3 wrong key test failed.");
-			}
-			catch (PgpException)
-			{
-				// expected
-			}
-
-			//
-			// certifications
-			//
-			PgpSignatureGenerator sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1);
-
-			sGen.InitSign(PgpSignature.KeyRevocation, pgpPrivKey);
-
-			PgpSignature sig = sGen.GenerateCertification(secretKey.PublicKey);
-
-			sig.InitVerify(secretKey.PublicKey);
-
-			if (!sig.VerifyCertification(secretKey.PublicKey))
-			{
-				Fail("revocation verification failed.");
-			}
-
-			PgpSecretKeyRing pgpDSAPriv = new PgpSecretKeyRing(dsaKeyRing);
-			PgpSecretKey secretDSAKey = pgpDSAPriv.GetSecretKey();
-			PgpPrivateKey pgpPrivDSAKey = secretDSAKey.ExtractPrivateKey(dsaPass);
-
-			sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1);
-
-			sGen.InitSign(PgpSignature.SubkeyBinding, pgpPrivDSAKey);
-
-			PgpSignatureSubpacketGenerator    unhashedGen = new PgpSignatureSubpacketGenerator();
-			PgpSignatureSubpacketGenerator    hashedGen = new PgpSignatureSubpacketGenerator();
-
-			hashedGen.SetSignatureExpirationTime(false, TEST_EXPIRATION_TIME);
-			hashedGen.SetSignerUserId(true, TEST_USER_ID);
-			hashedGen.SetPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS);
-			hashedGen.SetPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);
-			hashedGen.SetPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
-
-			sGen.SetHashedSubpackets(hashedGen.Generate());
-			sGen.SetUnhashedSubpackets(unhashedGen.Generate());
-
-			sig = sGen.GenerateCertification(secretDSAKey.PublicKey, secretKey.PublicKey);
+    [TestFixture]
+    public class PgpSignatureTest
+        : SimpleTest
+    {
+        private const int[] NO_PREFERENCES = null;
+        private static readonly int[] PREFERRED_SYMMETRIC_ALGORITHMS
+            = new int[] { (int)SymmetricKeyAlgorithmTag.Aes128, (int)SymmetricKeyAlgorithmTag.TripleDes };
+        private static readonly int[] PREFERRED_HASH_ALGORITHMS
+            = new int[] { (int)HashAlgorithmTag.Sha1, (int)HashAlgorithmTag.Sha256 };
+        private static readonly int[] PREFERRED_COMPRESSION_ALGORITHMS
+            = new int[] { (int)CompressionAlgorithmTag.ZLib };
+
+        private const int TEST_EXPIRATION_TIME = 10000;
+        private const string TEST_USER_ID = "test user id";
+        private static readonly byte[] TEST_DATA = Encoding.ASCII.GetBytes("hello world!\nhello world!\n");
+        private static readonly byte[] TEST_DATA_WITH_CRLF = Encoding.ASCII.GetBytes("hello world!\r\nhello world!\r\n");
+
+        private static readonly byte[] dsaKeyRing = Base64.Decode(
+            "lQHhBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ"
+            + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV"
+            + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/"
+            + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug"
+            + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu"
+            + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ"
+            + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz"
+            + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej"
+            + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbP4DAwIDIBTxWjkC"
+            + "GGAWQO2jy9CTvLHJEoTO7moHrp1FxOVpQ8iJHyRqZzLllO26OzgohbiPYz8u9qCu"
+            + "lZ9Xn7QzRXJpYyBFY2hpZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNh"
+            + "c3RsZS5vcmc+iFkEExECABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j"
+            + "9enEyjRDAlwAnjTjjt57NKIgyym7OTCwzIU3xgFpAJ0VO5m5PfQKmGJRhaewLSZD"
+            + "4nXkHg==");
+
+        private static readonly char[] dsaPass = "hello world".ToCharArray();
+
+        private static readonly byte[] rsaKeyRing = Base64.Decode(
+              "lQIEBEBXUNMBBADScQczBibewnbCzCswc/9ut8R0fwlltBRxMW0NMdKJY2LF"
+            + "7k2COeLOCIU95loJGV6ulbpDCXEO2Jyq8/qGw1qD3SCZNXxKs3GS8Iyh9Uwd"
+            + "VL07nMMYl5NiQRsFB7wOb86+94tYWgvikVA5BRP5y3+O3GItnXnpWSJyREUy"
+            + "6WI2QQAGKf4JAwIVmnRs4jtTX2DD05zy2mepEQ8bsqVAKIx7lEwvMVNcvg4Y"
+            + "8vFLh9Mf/uNciwL4Se/ehfKQ/AT0JmBZduYMqRU2zhiBmxj4cXUQ0s36ysj7"
+            + "fyDngGocDnM3cwPxaTF1ZRBQHSLewP7dqE7M73usFSz8vwD/0xNOHFRLKbsO"
+            + "RqDlLA1Cg2Yd0wWPS0o7+qqk9ndqrjjSwMM8ftnzFGjShAdg4Ca7fFkcNePP"
+            + "/rrwIH472FuRb7RbWzwXA4+4ZBdl8D4An0dwtfvAO+jCZSrLjmSpxEOveJxY"
+            + "GduyR4IA4lemvAG51YHTHd4NXheuEqsIkn1yarwaaj47lFPnxNOElOREMdZb"
+            + "nkWQb1jfgqO24imEZgrLMkK9bJfoDnlF4k6r6hZOp5FSFvc5kJB4cVo1QJl4"
+            + "pwCSdoU6luwCggrlZhDnkGCSuQUUW45NE7Br22NGqn4/gHs0KCsWbAezApGj"
+            + "qYUCfX1bcpPzUMzUlBaD5rz2vPeO58CDtBJ0ZXN0ZXIgPHRlc3RAdGVzdD6I"
+            + "sgQTAQIAHAUCQFdQ0wIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQs8JyyQfH"
+            + "97I1QgP8Cd+35maM2cbWV9iVRO+c5456KDi3oIUSNdPf1NQrCAtJqEUhmMSt"
+            + "QbdiaFEkPrORISI/2htXruYn0aIpkCfbUheHOu0sef7s6pHmI2kOQPzR+C/j"
+            + "8D9QvWsPOOso81KU2axUY8zIer64Uzqc4szMIlLw06c8vea27RfgjBpSCryw"
+            + "AgAA");
+
+        private static readonly char[] rsaPass = "2002 Buffalo Sabres".ToCharArray();
+
+        private static readonly byte[] nullPacketsSubKeyBinding = Base64.Decode(
+            "iDYEGBECAAAAACp9AJ9PlJCrFpi+INwG7z61eku2Wg1HaQCgl33X5Egj+Kf7F9CXIWj2iFCvQDo=");
+
+        private static readonly byte[] okAttr = Base64.Decode(
+                "mQENBFOkuoMBCAC+8WcWLBZovlR5pLW4tbOoH3APia+poMEeTNkXKe8yAH0f"
+              + "ZmTQgeXFBIizd4Ka1QETbayv+C6Axt6Ipdwf+3N/lqcOqg6PEwuIX4MBrv5R"
+              + "ILMH5QyM3a3RlyXa7xES3I9t2VHiZvl15OrTZe67YNGtxlXyeawt6v/9d/a3"
+              + "M1EaUzjN4H2EfI3P/VWpMUvQkn70996UKInOyaSB0hef/QS10jshG9DdgmLM"
+              + "1/mJFRp8ynZOV4yGLnAdoEoPGG/HJZEzWfqOiwmWZOIrZIwedY1eKuMIhUGv"
+              + "LTC9u+9X0h+Y0st5eb1pf8OLvrpRpEyHMrxXfj/V3rxom4d160ifGihPABEB"
+              + "AAG0GndpdGggYXR0dHIgPGF0dHJAYXR0ci5uZXQ+iQE4BBMBAgAiBQJTpLqD"
+              + "AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBCjbg0bKVgCXJiB/wO"
+              + "6ksdrAy+zVxygFhk6Ju2vpMAOGnLl1nqBVT1mA5XiJu3rSiJmROLF2l21K0M"
+              + "BICZfz+mjIwN56RZNzZnEmXk/E2+PgADV5VTRRsjqlyoeN/NrLWuTm9FyngJ"
+              + "f96jVPysN6FzYRUB5Fuys57P+nu0RMoLGkHmQhp4L5hgNJTBy1SRnXukoIgJ"
+              + "2Ra3EBQ7dBrzuWW1ycwU5acfOoxfcVqgXkiXaxgvujFChZGWT6djbnbbzlMm"
+              + "sMKr6POKChEPWo1HJXXz1OaPsd75JA8bImgnrHhB3dHhD2wIqzQrtTxvraqz"
+              + "ZWWR2xYZPltzBSlaAdn8Hf0GGBoMhutb3tJLzbAX0cybzJkBEAABAQAAAAAA"
+              + "AAAAAAAAAP/Y/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwAKBwcIBwYKCAgICwoK"
+              + "Cw4YEA4NDQ4dFRYRGCMfJSQiHyIhJis3LyYpNCkhIjBBMTQ5Oz4+PiUuRElD"
+              + "PEg3PT47/9sAQwEKCwsODQ4cEBAcOygiKDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7"
+              + "Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7/8AAEQgAkAB4AwEiAAIR"
+              + "AQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIB"
+              + "AwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHw"
+              + "JDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVm"
+              + "Z2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4"
+              + "ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8B"
+              + "AAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQE"
+              + "AAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDTh"
+              + "JfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2"
+              + "d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG"
+              + "x8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A"
+              + "9moqtf30Gm2cl3cvtijGSa4a++LNlGStlZvKR0ZuBWkKU6nwomU4x3PQqK8g"
+              + "uPinrEzYhhihX86ns/Ffia/XzElJUHOV4/rW/wBUqJXlZEe2i9j1iivMP+Ex"
+              + "1q3+/KCw6gip4PiXdREC5tUkHcrwaTwlVbK4e1iekUVzmheNdO1ycWyK8U5G"
+              + "drf410QOa55RcXaSNE09ULRRRUjCiiigAooooAKKKKAOY+IblfCN1g9cA/rX"
+              + "h1fQPiXT4dU0o2dwXEcrclCARgE8ZB9K4J/AGkKeJr38ZU/+Ir0MLiIUoNSO"
+              + "erTlJ3R54v3hXpfg3UdNGmrHPMsToOc9+KrQeBdAd2SS7vkYdPnX/wCIqy3g"
+              + "fRoThb+9GP8AaQ/+yVdavRqxs2yYU5wdzH164t57+V7XHlZOCOh5rn5n5Ndr"
+              + "J4U0xBt/tC8x16p/8RTP+EK0uRQ32q9IPfzE/wDiKuGKpRSSYnSm3c5/wjP5"
+              + "XiKFywUDqScelevR6/pCR4k1S0DDqPOXI/WvPLjwdplpbtPG9zI6so2yspU5"
+              + "YDoFHrW7pOmRWpEiqVyuPlHH41xYmPgpPmibU4uKszqY9f0aZtseq2bN6eeu"
+              + "f51fVldQyMGU9CDkGueMCOpxtYe3NYWoabJJOZrWV7V1yFe1cxnH1HX8a57G"
+              + "lz0CiuFg8U6rpjql2PtkXTMgCv8Agw4/MfjXU6VrthrCH7NKRIoy8LjDr+Hp"
+              + "7jIosFzRooopDCiiigClqXKRD1c/+gtWPLFitnUfuRH/AG//AGUiqDKGFAzA"
+              + "mFzG7rGhAJJyB604XtzGGjeAuD3GR2x260t1fTJf3EChAsLKo+XOcorZP/fV"
+              + "Qm8lPXZ/3yKLCJDPIBsjUjIHUewFWoYWS2jDDBArPN1IQR8o/wCAirdvcERw"
+              + "u33ZYkdgOgLKCcfnRYBL0f8AEvmz6x/+jUqxbzyCLCKoC92NRaiMWLkHhmj/"
+              + "AB+dTWlarutdoIXI64oQETXJ25MbA9DsolCEY4zjpVswL5QXgMB1xWZMRDIy"
+              + "woJn6HnAWmIzb+GZyyIisD0Vl4Nc5I0ulXSO8zQtnMTrkGM/71dVNpufnMkm"
+              + "7Odwfmqd5CGi8tuQB0b5v51SEzf8M+Kl1QixvdqXoHysOFmA7j0PqPxHoOlr"
+              + "xm5DROrRkxvGQVZOCpHQivSPCfiEa9px80gXlvhZ1Hf0Yex/mDRKNtQTN6ii"
+              + "ioKKmoD9zGfSVf1OP61QrUuovOgZM4PBB9CDkH865PxJrVx4d057yS0inAcI"
+              + "qq5TJJ+hoAqXg/4m9/8A9dU/9FR1CRUGlan/AG7Fcal9n+z+dNjy9+/btRV6"
+              + "4GemelWiKoRHVuIf6Ha/9e0X/oC1VIrIt/FtxNGsFtoxk+zoITI1zhWKjbn7"
+              + "vt0zSYzfvJSLAIennIB+p/pWtZy4hXmuQa71fUzGhtre1jR920MXLHGMk+2T"
+              + "6da1oZb22ULM6FDwGCkHNFhGzNqCbjAmXkPGF7VJFAkEQHBNQWkMUcQIwc85"
+              + "9fepJJeOtNIVyK4bg1jXjda0LiTg1k3b9atEsxr3qai0LWDoOvQXpYiEny5x"
+              + "6oep/Dg/hT7s9ayLoZVs1VriPeQcjIorC8F37ah4Vs3kbdLCvkyexXjn3xg/"
+              + "jRWBqb1ee/FqYLpun24P+snLMPoOK9Crzb4uKQumSfwl2H44qo7iexB4JQHR"
+              + "wCMj7Q39K2roRRXTkqPLU8iuB8NFl8S6ftdgrSHIycH5T2rvb8b2uap6MS1R"
+              + "DJcWsq7YUCt65J4rA0FUCHKjh2/9CNYfjDUSkS2lskrlHDTSR/8ALPjocUaH"
+              + "4msUtVjCM0qLyqkAH8TyKSBnoELoOgFJf3VoITFcTBNy546gevtzXM6Rqd3f"
+              + "akWadyigsYw3y+gAH410O/PDZHHcU7E3LWnXED2SC2nE0ajG4HJ/GpJJeOtY"
+              + "lxYpJdxXMcssLxkE+SwXdj14qrf6jrP22SK0t4RFkFZZMYx/n8aANieXg1mX"
+              + "MnWla5lKRCSMFmB8xoz8qHHvzg1TnlzVIRTuW61l3MyQRSTuNwjXdt9T2FXZ"
+              + "3zWfcRpPG8Mn3JBtJ9PQ/nVCO7+Dl49z4f1BJG3Mt6XJ/wB5V/woqD4LwvDp"
+              + "urK45W5VT9QtFYPc1Wx6VXDfFi0M3hmG6A5trhSfoRj/AAruaz9d01dY0O80"
+              + "9v8AlvEVX2bt+uKFowZ4z4Zbd4h04/8ATRv/AEBq7+T53ufrXnXhffF4ls4J"
+              + "QVkildWB7EKwNehwnfLcD/aFXLcUThGs5bDUpYrgFWZ2dGHR1J6ip57C0voR"
+              + "HcQq6htwI+Ug4xkEVo+MJ0jksrYA+ZuMhPouMfzP6VnQyEqKqOqJejMmfSr/"
+              + "AE8NNbzC6hjG7aQVlA/kcVueFtR+12Mrpceagk4Abdt4/rUiMeOeaqS6UhuV"
+              + "ubSaWymxtdrbC+YvoR6+9FhHRPcCNGaRgiqNzFjgAVmya/pYkZftSnH8QQlT"
+              + "9D3rmdbefT4o7KO6ne3ky+yV9xBB9euO+Kw2mfruNAj0OW8t/K837TB5eM7/"
+              + "ADBjFVp3IAOQQwyCDkEexrz95W9vrirula1LYyiOQu9s2Q0YPT3GehpgdJK2"
+              + "apzt8hottQgv1k8pZEeMZIYg5GcZyKjuFkkKQxKXklYKijqSeAKdwPUvhdbe"
+              + "X4ZmutpH2y7eUZ9AAv8ANTRXSaJpqaPotnpyYP2eIKxHdv4j+JyaKwe5qi/R"
+              + "RRSGeaeJ/Dx03x7Yavbr/o967eZj+GQI38xz+dXdPffczD1cVu+Lzi0tT6Tj"
+              + "/wBBNc3oz7r5x6uKroIwPFt5BeazFbQKGa1BWSQdycfL+GP1qCCPgU3+yprC"
+              + "/ltrpcSqxOezAnhge9aMNv04rRaIh7jEiNSSFLeF55c7I1LNjrgVcjt/alu9"
+              + "O+12U1uSUEqFNyjlcjrRcVjzzVL6bU5xJIioqjCIo4Uf1NUDEfStiXTLizuH"
+              + "tboL5qc7l6OvZhTTZ+1K4WMZoSe1NFuSelbP2M9xT47As2FXJp3FYqaUptJ2"
+              + "fZu3IVwSR1r0L4f6FHqmsf2w8bC3sjhA2CGlx29duc/UisHQ/DlzreoiwtPl"
+              + "24NxPjKwL/Vj2H9K9m07T7bStPhsbOPy4IV2qO/uT6knkmoky4otUUUVBYUU"
+              + "UUAc54yP+hWv/XwB+hrntOTyNbSP+84rs9Z04ajaqu7a8bh0OMjI9a5O6gvo"
+              + "b3zjZAuDwyOMfryKaegEHjZTYva6qV8yFf3MqKMsueQw9uDmq+nPZahGJLSd"
+              + "Hz2zyKsXEOpagyC4IWOM5WNOmfUnuaxtT8NOJPtFoGt5uu6PjP4U0xNHSx2b"
+              + "jtmrC2p/u1xEOr+J9MO1sXCj++OavxeO9Tj4m0vJ9jTuI09c8NrqUavGfKuI"
+              + "/wDVyhc49iO4rnToV/A/lXCI5xkPGCFI/HvWhL491BhiLSufc1l6hrXiTVZQ"
+              + "IALaPGOFyfc0gHzadBZxGW9nSFBydxp+nafPrEii0RrOyP3rmRfncf7Cn+Z/"
+              + "Wo9K8NXEl0Lm+L3EgOQZTux9K7W0s5BgYNFwsbOg2tlpVilnYxCOMHJ7s7Hq"
+              + "xPc1sqcjNZNnbsuM1qoMLUlD6KKKACiiigBCM1E9tG55UVNRQBWNlF2UVC+m"
+              + "xP8Aw1fooAx5NDgfqg/KoG8N2p/5ZL+Vb9FAHPjw1ag/6pfyqZNBt06IPyra"
+              + "ooAzU0qJOiirCWcadBVqigBixhegp1LRQAUUUUAf/9mJATgEEwECACIFAlOk"
+              + "xL4CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEEKNuDRspWAJhi8I"
+              + "AKhIemGlaKtuZxBA4bQcJOy/ZdGJriJuu3OQl2m6CAwxaGMncpxHFVTT6GqI"
+              + "Vu4/b4SSwYP1pI24MqAkdEudjFSi15ByogPFpUoDJC44zrO64b/mv3L5iq1C"
+              + "PY+VvgLMAdvA3Tsoj/rNYlD0fieBa9EF8BtoAkaA4X6pihNPGsVe0AxlJhQw"
+              + "eMgLXwTjllJm1iWa/fEQvv5Uk01gzayH1TIwkNAJ0E8s6Ontu2szUHjFGRNA"
+              + "llR5OJzt/loo9p53zWddFfxlCfn2w+smHyB4i+FfpQfFSMLnwew7wncHs6XE"
+              + "PevLPcW66T3w2/oMd0fC7GwhnCiebDYjl8ymF+4b0N65AQ0EU6S6gwEIAOAC"
+              + "NRzXH0dc5wwkucFdTMs1nxr16y+Kk3zF3R21OkHLHazXVC7ZP2HurTFGd5VP"
+              + "Yd+vv0CrYHCjjMu0lIeMfTlpJswvJRBxVw8vIVLpOSqxtJS+zysE8/LpKw6i"
+              + "ti51ydalhm6VYGPm+OAoAAO1pLriwR132caoye5vqxGKEUCmkaNLl8LCljyH"
+              + "kMgL5nQr+7cerTcGd2MaC8Y5vQuZBpVVBZcVt004iP3bCJu2l2RKskIoSysC"
+              + "68bqV4XLMnoVeM97VPdwdb0Y7tGXCW8YodN8ni43YOaQxfr7fHx8nyzQ5S8w"
+              + "a701GKWcQqCb0DR1ngCRAgWLzj8HDlZoofPL8d0AEQEAAYkBHwQYAQIACQUC"
+              + "U6S6gwIbDAAKCRBCjbg0bKVgCWPSB/wN9Z5ayWiox5xxouAQv4W2JZGPiqk8"
+              + "nFF5fzSgQxV4Xo63IaC1bD8411pgRlj1aWtt8pvWjEW9WWxvyPnkz0xldErb"
+              + "NRZ9482TknY0dsrbmg6jwLOlNvLhLVhWUWt+DkH20daVCADV/0p2/2OPodn+"
+              + "MYnueL5ljoJxzTO84WMz1u7qumMdX4EcLAFblelmPsGiNsnGabc148+TgYZI"
+              + "1fBucn5Xrk4fxVCuqa8QjOa37aHHT5Li/xGIDCbtCqPPIi7M7O1yq8gXLWP9"
+              + "TV7nsu99t4EiZT4zov9rCS+tgvBiFrRqsHL37PGrS27s+gMw3GR7F6BiDiqa"
+              + "0GvLdt0Lx24c"
+            );
+
+        private static readonly byte[] attrLongLength = Base64.Decode(
+                "mQENBEGz0vIBCADLb2Sb5QbOhRIzfOg3u9F338gK1XZWJG8JwXP8DSGbQEof"
+              + "0+YoT/7bA+3h1ljh3LG0m8JUEdolrxLz/8Mguu2TA2UQiMwRaRChSVvBgkCR"
+              + "Ykr97+kClNgmi+PLuUN1z4tspqdE761nRVvUl2x4XvLTJ21hU5eXGGsC+qFP"
+              + "4Efe8B5kH+FexAfnFPPzou3GjbDbYv4CYi0pyhTxmauxyJyQrQ/MQUt0RFRk"
+              + "L8qCzWCR2BmH3jM3M0Wt0oKn8C8+fWItUh5U9fzv/K9GeO/SV8+zdL4MrdqD"
+              + "stgqXNs27H+WeIgbXlUGIs0mONE6TtKZ5PXG5zFM1bz1vDdAYbY4eUWDABEB"
+              + "AAGJAhwEHwEIAAYFAlLd55oACgkQ5ppjUk3RnxANSBAAqzYF/9hu7x7wtmi7"
+              + "ScmIal6bXP14ZJaRVibMnAPEPIHAULPVa8x9QX/fGW8px5tK9YU41wigLXe6"
+              + "3eC5MOLc+wkouELsBeeA3zap51/5HhsuHq5AYtL2tigce9epYUVNV9LaZd2U"
+              + "vQOQ6RqyTMhSADN9mD0kR+Nu1+ns7Ur7qAq6UI39hFIGKPoZQ61pTrVsi8N7"
+              + "GxHoNwa1FAxm0Dm4XvyiJHPOYs0K4OnNWLKLCcSVOx453Zj3JnllRrCFLpIt"
+              + "H27jAxcbGStxWpJvlVMSylcP/x0ATjGfp+kSv2TpU2wK0W5iUtrn30W+WZp4"
+              + "+BIXL0NSi4XPksoUoM9dOTsOCPh/ntiWJBlzIdhQuxgcwymoYnaAG0ermI+R"
+              + "djB0gCj0AfMDZEOW+thFKg1kEkYrUnAISNDt+VZNUtk26tJ7PDitC9EY6IA6"
+              + "vbKeh47LmqpyK3gqQiIA/XuWhdUOr1Wv3H8qxumFjxQQh9sr72IbWFJ+tSNl"
+              + "UtrohK7N6CoJQidkj2qFsuGLcFKypAdS7Y0s0t9uOYJLwj1c+2KG0mrA2PvW"
+              + "1vng9mMN6AHIx9oRSwQc1+OV29ws2hfNB3JQnpdzBYAy8C5haUWG7E7WFg+j"
+              + "pNpeREVX0S+1ibmWDVs+trSQI8hd58j91Kc2YvwE13YigC9nlU2R853Gsox4"
+              + "oazn75iJAhwEHwEIAAYFAlMkBMIACgkQcssEwQwvQ5L2yxAAmND9w3OZsJpF"
+              + "tTAJFpfg8Bacy0Xs/+LipA1hB8vG+mvaiedcqc5KTpuFQ4bffH1swMRjXAM7"
+              + "ZP/u/6qX2LL9kjxCtwDUjDT8YcphTnRxSu5Jv3w4Rf0zWIRWHhnbswiBuGwE"
+              + "zQN8V20AYxfZ+ffkR0wymm/y8qLQ1oNynweijXHSlaG/sVmvDxkuc77n4hLi"
+              + "4UVQiSAP7dRIkcOh6QCBW4TxoZkDfxIhASFQWl1paCagO1rwyo7YY42O4c16"
+              + "+UZBMZtWTvRO2rThz1g9SxAyx8FZ7SxMv140C7VGQmdag97dA1WgBOCuLzLi"
+              + "cYT+o/bL9vpFXSI7LVflQEqauzL4fs2X8ggckoI4lkjcDe8DhiDmCoju5Lat"
+              + "Q/7DqV8T6z/Gv0sK2hqKr4ULC3By4N11WDCg6wXa72tMQoFBT1vOC+UzLHOj"
+              + "vgWBJKE7q3E7kFfq22D0ZX0BPTYy2mcrghMzvvOe74Dx495zlUJhtBfr8MC2"
+              + "uPnjsv6PjCYAaomQcvvI0o/5k8JIFi1P0pwLM5VjfujdAuCpAwQuy9AeGlz2"
+              + "TEuZZlWBZuyBqZ7JyHx5xz1aVXbY7kofqO+njyyZ+MakZRLYpBI+B/8KomQP"
+              + "pqWVARw4uPAXVTd1fjW2CTQtt7Ia6BRWMSblxTv3VWosTSgPnCXmzYEpGvCL"
+              + "bIauL8UEhzS0JVBHUCBHbG9iYWwgRGlyZWN0b3J5IFZlcmlmaWNhdGlvbiBL"
+              + "ZXmJAV4EEAECAEAFAkJRtHAHCwkIBwMCCgIZARkYbGRhcDovL2tleXNlcnZl"
+              + "ci5wZ3AuY29tBRsDAAAAAxYCAQUeAQAAAAQVCAIKABIJEJcQuJvKV618B2VH"
+              + "UEcAAQH35ggAnVHdAh2KqrvwSnPos73YdlVbeF9Lcbxs4oYPDCk6AHiDpjr2"
+              + "nxu48i1BiLea7aTEEwwAkcIa/3lCLP02NjGXq5gRnWpW/d0xtsaDDj8yYWus"
+              + "WGhEJsUlrq5Cz2KjwxNQHXRhHXEDR8vq9uzw5EjCB0u69vlwNmo8+fa17YMN"
+              + "VdXaXsmXJlJciVHazdvGoscTzZOuKDHdaJmY8nJcCydk4qsFOiGOcFm5UOKP"
+              + "nzdBh31NKglqw/xh+1nTA2z5orsY4jVFIB6sWqutIcVQYt/J78diAKFemkEO"
+              + "Qe0kU5JZrY34E8pp4BmS6mfPyr8NtHFfMOAE4m8acFeaZK1X6+uW57QpRE5S"
+              + "IEtTMSA8ZG8tbm90LXJlcGx5QGtleXNlcnZlcjEucGdwLmNvbT6JAVMEEAEC"
+              + "AD0FAkmgVoIHCwkIBwMCChkYbGRhcDovL2tleXNlcnZlci5wZ3AuY29tBRsD"
+              + "AAAAAxYCAQUeAQAAAAQVCAIKAAoJEJcQuJvKV618t6wH/1RFTp9Z7QUZFR5h"
+              + "r8eHFWhPoeTCMXF3Vikgw2mZsjN43ZyzpxrIdUwwHROQXn1BzAvOS0rGNiDs"
+              + "fOOmQFulz+Oc14xxGox2TZbdnDnXEb8ReZnimQCWYERfpRtY6GSY7uWzNjG2"
+              + "dLB1y3XfsOBG+QqTULSJSZqRYD+2IpwPlAdl6qncqRvFzGcPXPIp0RS6nvoP"
+              + "Jfe0u2sETDRAUDwivr7ZU/xCA12txELhcsvMQP0fy0CRNgN+pQ2b6iBL2x1l"
+              + "jHgSG1r3g3gQjHEk3UCTEKHq9+mFhd/Gi0RXz6i1AmrvW4pKhbtN76WrXeF+"
+              + "FXTsB09f1xKnWi4c303Ms1tIJQC0KUROUi1LUzIgPGRvLW5vdC1yZXBseUBr"
+              + "ZXlzZXJ2ZXIyLnBncC5jb20+iQFTBBABAgA9BQJJoFabBwsJCAcDAgoZGGxk"
+              + "YXA6Ly9rZXlzZXJ2ZXIucGdwLmNvbQUbAwAAAAMWAgEFHgEAAAAEFQgCCgAK"
+              + "CRCXELibyletfBwzB/41/OkBDVLgEYnGJ78rKHLtgMdRfrL8gmZn9KhMi44H"
+              + "nlFl1NAgi1yuWA2wC8DziVKIiu8YCaCVP0FFXuBK1BF8uZDRp8lZuT3Isf0/"
+              + "4DX4yuvZwY5nmtDu3qXrjZ7bZi1W2A8c9Hgc+5A30R9PtiYy5Lz2m8xZl4P6"
+              + "wDrYCQA2RLfzGC887bIPBK/tvXTRUFZfj2X1o/q4pr8z4NJTaFUl/XrseGcJ"
+              + "R2PP3S2/fU5LErqLJhlj690xofRkf9oYrUiyyb1/UbWmNJsOHSHyy8FEc9lv"
+              + "lSJIa39niSQKK6I0Mh1LheXNL7aG152KkXiH0mi6bH4EOzaTR7dfLey3o9Ph"
+              + "0cye/wAADVkBEAABAQAAAAAAAAAAAAAAAP/Y/+AAEEpGSUYAAQEAAAEAAQAA"
+              + "/9sAQwAKBwcIBwYKCAgICwoKCw4YEA4NDQ4dFRYRGCMfJSQiHyIhJis3LyYp"
+              + "NCkhIjBBMTQ5Oz4+PiUuRElDPEg3PT47/9sAQwEKCwsODQ4cEBAcOygiKDs7"
+              + "Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7"
+              + "Ozs7/8AAEQgAkAB4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAAB"
+              + "AgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNR"
+              + "YQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNE"
+              + "RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeY"
+              + "mZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn"
+              + "6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkK"
+              + "C//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRC"
+              + "kaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNU"
+              + "VVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWm"
+              + "p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX2"
+              + "9/j5+v/aAAwDAQACEQMRAD8A9moqtf30Gm2cl3cvtijGSa4a++LNlGStlZvK"
+              + "R0ZuBWkKU6nwomU4x3PQqK8guPinrEzYhhihX86ns/Ffia/XzElJUHOV4/rW"
+              + "/wBUqJXlZEe2i9j1iivMP+Ex1q3+/KCw6gip4PiXdREC5tUkHcrwaTwlVbK4"
+              + "e1iekUVzmheNdO1ycWyK8U5Gdrf410QOa55RcXaSNE09ULRRRUjCiiigAooo"
+              + "oAKKKKAOY+IblfCN1g9cA/rXh1fQPiXT4dU0o2dwXEcrclCARgE8ZB9K4J/A"
+              + "GkKeJr38ZU/+Ir0MLiIUoNSOerTlJ3R54v3hXpfg3UdNGmrHPMsToOc9+KrQ"
+              + "eBdAd2SS7vkYdPnX/wCIqy3gfRoThb+9GP8AaQ/+yVdavRqxs2yYU5wdzH16"
+              + "4t57+V7XHlZOCOh5rn5n5NdrJ4U0xBt/tC8x16p/8RTP+EK0uRQ32q9IPfzE"
+              + "/wDiKuGKpRSSYnSm3c5/wjP5XiKFywUDqScelevR6/pCR4k1S0DDqPOXI/Wv"
+              + "PLjwdplpbtPG9zI6so2yspU5YDoFHrW7pOmRWpEiqVyuPlHH41xYmPgpPmib"
+              + "U4uKszqY9f0aZtseq2bN6eeuf51fVldQyMGU9CDkGueMCOpxtYe3NYWoabJJ"
+              + "OZrWV7V1yFe1cxnH1HX8a57Glz0CiuFg8U6rpjql2PtkXTMgCv8Agw4/MfjX"
+              + "U6VrthrCH7NKRIoy8LjDr+Hp7jIosFzRooopDCiiigClqXKRD1c/+gtWPLFi"
+              + "tnUfuRH/AG//AGUiqDKGFAzAmFzG7rGhAJJyB604XtzGGjeAuD3GR2x260t1"
+              + "fTJf3EChAsLKo+XOcorZP/fVQm8lPXZ/3yKLCJDPIBsjUjIHUewFWoYWS2jD"
+              + "DBArPN1IQR8o/wCAirdvcERwu33ZYkdgOgLKCcfnRYBL0f8AEvmz6x/+jUqx"
+              + "bzyCLCKoC92NRaiMWLkHhmj/AB+dTWlarutdoIXI64oQETXJ25MbA9DsolCE"
+              + "Y4zjpVswL5QXgMB1xWZMRDIywoJn6HnAWmIzb+GZyyIisD0Vl4Nc5I0ulXSO"
+              + "8zQtnMTrkGM/71dVNpufnMkm7Odwfmqd5CGi8tuQB0b5v51SEzf8M+Kl1Qix"
+              + "vdqXoHysOFmA7j0PqPxHoOlrxm5DROrRkxvGQVZOCpHQivSPCfiEa9px80gX"
+              + "lvhZ1Hf0Yex/mDRKNtQTN6iiioKKmoD9zGfSVf1OP61QrUuovOgZM4PBB9CD"
+              + "kH865PxJrVx4d057yS0inAcIqq5TJJ+hoAqXg/4m9/8A9dU/9FR1CRUGlan/"
+              + "AG7Fcal9n+z+dNjy9+/btRV64GemelWiKoRHVuIf6Ha/9e0X/oC1VIrIt/Ft"
+              + "xNGsFtoxk+zoITI1zhWKjbn7vt0zSYzfvJSLAIennIB+p/pWtZy4hXmuQa71"
+              + "fUzGhtre1jR920MXLHGMk+2T6da1oZb22ULM6FDwGCkHNFhGzNqCbjAmXkPG"
+              + "F7VJFAkEQHBNQWkMUcQIwc859fepJJeOtNIVyK4bg1jXjda0LiTg1k3b9atE"
+              + "sxr3qai0LWDoOvQXpYiEny5x6oep/Dg/hT7s9ayLoZVs1VriPeQcjIorC8F3"
+              + "7ah4Vs3kbdLCvkyexXjn3xg/jRWBqb1ee/FqYLpun24P+snLMPoOK9Crzb4u"
+              + "KQumSfwl2H44qo7iexB4JQHRwCMj7Q39K2roRRXTkqPLU8iuB8NFl8S6ftdg"
+              + "rSHIycH5T2rvb8b2uap6MS1RDJcWsq7YUCt65J4rA0FUCHKjh2/9CNYfjDUS"
+              + "kS2lskrlHDTSR/8ALPjocUaH4msUtVjCM0qLyqkAH8TyKSBnoELoOgFJf3Vo"
+              + "ITFcTBNy546gevtzXM6Rqd3fakWadyigsYw3y+gAH410O/PDZHHcU7E3LWnX"
+              + "ED2SC2nE0ajG4HJ/GpJJeOtYlxYpJdxXMcssLxkE+SwXdj14qrf6jrP22SK0"
+              + "t4RFkFZZMYx/n8aANieXg1mXMnWla5lKRCSMFmB8xoz8qHHvzg1TnlzVIRTu"
+              + "W61l3MyQRSTuNwjXdt9T2FXZ3zWfcRpPG8Mn3JBtJ9PQ/nVCO7+Dl49z4f1B"
+              + "JG3Mt6XJ/wB5V/woqD4LwvDpurK45W5VT9QtFYPc1Wx6VXDfFi0M3hmG6A5t"
+              + "rhSfoRj/AAruaz9d01dY0O809v8AlvEVX2bt+uKFowZ4z4Zbd4h04/8ATRv/"
+              + "AEBq7+T53ufrXnXhffF4ls4JQVkildWB7EKwNehwnfLcD/aFXLcUThGs5bDU"
+              + "pYrgFWZ2dGHR1J6ip57C0voRHcQq6htwI+Ug4xkEVo+MJ0jksrYA+ZuMhPou"
+              + "MfzP6VnQyEqKqOqJejMmfSr/AE8NNbzC6hjG7aQVlA/kcVueFtR+12Mrpcea"
+              + "gk4Abdt4/rUiMeOeaqS6UhuVubSaWymxtdrbC+YvoR6+9FhHRPcCNGaRgiqN"
+              + "zFjgAVmya/pYkZftSnH8QQlT9D3rmdbefT4o7KO6ne3ky+yV9xBB9euO+Kw2"
+              + "mfruNAj0OW8t/K837TB5eM7/ADBjFVp3IAOQQwyCDkEexrz95W9vrirula1L"
+              + "YyiOQu9s2Q0YPT3GehpgdJK2apzt8hottQgv1k8pZEeMZIYg5GcZyKjuFkkK"
+              + "QxKXklYKijqSeAKdwPUvhdbeX4ZmutpH2y7eUZ9AAv8ANTRXSaJpqaPotnpy"
+              + "YP2eIKxHdv4j+JyaKwe5qi/RRRSGeaeJ/Dx03x7Yavbr/o967eZj+GQI38xz"
+              + "+dXdPffczD1cVu+Lzi0tT6Tj/wBBNc3oz7r5x6uKroIwPFt5BeazFbQKGa1B"
+              + "WSQdycfL+GP1qCCPgU3+yprC/ltrpcSqxOezAnhge9aMNv04rRaIh7jEiNSS"
+              + "FLeF55c7I1LNjrgVcjt/alu9O+12U1uSUEqFNyjlcjrRcVjzzVL6bU5xJIio"
+              + "qjCIo4Uf1NUDEfStiXTLizuHtboL5qc7l6OvZhTTZ+1K4WMZoSe1NFuSelbP"
+              + "2M9xT47As2FXJp3FYqaUptJ2fZu3IVwSR1r0L4f6FHqmsf2w8bC3sjhA2CGl"
+              + "x29duc/UisHQ/DlzreoiwtPl24NxPjKwL/Vj2H9K9m07T7bStPhsbOPy4IV2"
+              + "qO/uT6knkmoky4otUUUVBYUUUUAc54yP+hWv/XwB+hrntOTyNbSP+84rs9Z0"
+              + "4ajaqu7a8bh0OMjI9a5O6gvob3zjZAuDwyOMfryKaegEHjZTYva6qV8yFf3M"
+              + "qKMsueQw9uDmq+nPZahGJLSdHz2zyKsXEOpagyC4IWOM5WNOmfUnuaxtT8NO"
+              + "JPtFoGt5uu6PjP4U0xNHSx2bjtmrC2p/u1xEOr+J9MO1sXCj++OavxeO9Tj4"
+              + "m0vJ9jTuI09c8NrqUavGfKuI/wDVyhc49iO4rnToV/A/lXCI5xkPGCFI/HvW"
+              + "hL491BhiLSufc1l6hrXiTVZQIALaPGOFyfc0gHzadBZxGW9nSFBydxp+nafP"
+              + "rEii0RrOyP3rmRfncf7Cn+Z/Wo9K8NXEl0Lm+L3EgOQZTux9K7W0s5BgYNFw"
+              + "sbOg2tlpVilnYxCOMHJ7s7HqxPc1sqcjNZNnbsuM1qoMLUlD6KKKACiiigBC"
+              + "M1E9tG55UVNRQBWNlF2UVC+mxP8Aw1fooAx5NDgfqg/KoG8N2p/5ZL+Vb9FA"
+              + "HPjw1ag/6pfyqZNBt06IPyraooAzU0qJOiirCWcadBVqigBixhegp1LRQAUU"
+              + "UUAf/9mJAVYEEAECADgFAkJRtHAHCwkIBwMCChkYbGRhcDovL2tleXNlcnZl"
+              + "ci5wZ3AuY29tBRsDAAAAAxYCAQUeAQAAAAASCRCXELibyletfAdlR1BHAAEB"
+              + "SBIH/j+RGcMuHmVoZq4+XbmCunnbft4T0Ta4o6mxNkc6wk5P9PpcE9ixztjV"
+              + "ysMmv2i4Y746dCY9B1tfhQW10S39HzrYHh3I4a2wb9zQniZCf1XnbCe1eRss"
+              + "NhTpLVXXnXKEsc9EwD5MtiPICluZIXB08Zx2uJSZ+/i9TqSM5EUuJk+lXqgX"
+              + "GUiTaSXN63I/4BnbFzCw8SaST7d7nok45UC9I/+gcKVO+oYETgrsU7AL6uk1"
+              + "6YD9JpfYZHEFmpYoS+qQ3tLfPCG3gaS/djBZWWkNt5z7e6sbRko49XEj3EUh"
+              + "33HgjrOlL8uJNbhlZ5NeILcxHqGTHji+5wMEDBjfNT/C6m0=");
+
+        public override void PerformTest()
+        {
+            //
+            // RSA tests
+            //
+            PgpSecretKeyRing pgpPriv = new PgpSecretKeyRing(rsaKeyRing);
+            PgpSecretKey secretKey = pgpPriv.GetSecretKey();
+            PgpPrivateKey pgpPrivKey = secretKey.ExtractPrivateKey(rsaPass);
+
+            try
+            {
+                doTestSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+
+                Fail("RSA wrong key test failed.");
+            }
+            catch (PgpException)
+            {
+                // expected
+            }
+
+            try
+            {
+                doTestSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+
+                Fail("RSA V3 wrong key test failed.");
+            }
+            catch (PgpException)
+            {
+                // expected
+            }
+
+            //
+            // certifications
+            //
+            PgpSignatureGenerator sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1);
+
+            sGen.InitSign(PgpSignature.KeyRevocation, pgpPrivKey);
+
+            PgpSignature sig = sGen.GenerateCertification(secretKey.PublicKey);
+
+            sig.InitVerify(secretKey.PublicKey);
+
+            if (!sig.VerifyCertification(secretKey.PublicKey))
+            {
+                Fail("revocation verification failed.");
+            }
+
+            PgpSecretKeyRing pgpDSAPriv = new PgpSecretKeyRing(dsaKeyRing);
+            PgpSecretKey secretDSAKey = pgpDSAPriv.GetSecretKey();
+            PgpPrivateKey pgpPrivDSAKey = secretDSAKey.ExtractPrivateKey(dsaPass);
+
+            sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1);
+
+            sGen.InitSign(PgpSignature.SubkeyBinding, pgpPrivDSAKey);
+
+            PgpSignatureSubpacketGenerator    unhashedGen = new PgpSignatureSubpacketGenerator();
+            PgpSignatureSubpacketGenerator    hashedGen = new PgpSignatureSubpacketGenerator();
+
+            hashedGen.SetSignatureExpirationTime(false, TEST_EXPIRATION_TIME);
+            hashedGen.SetSignerUserId(true, TEST_USER_ID);
+            hashedGen.SetPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS);
+            hashedGen.SetPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);
+            hashedGen.SetPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
+
+            sGen.SetHashedSubpackets(hashedGen.Generate());
+            sGen.SetUnhashedSubpackets(unhashedGen.Generate());
+
+            sig = sGen.GenerateCertification(secretDSAKey.PublicKey, secretKey.PublicKey);
+
+            byte[] sigBytes = sig.GetEncoded();
+
+            PgpObjectFactory f = new PgpObjectFactory(sigBytes);
+
+            sig = ((PgpSignatureList) f.NextPgpObject())[0];
+
+            sig.InitVerify(secretDSAKey.PublicKey);
+
+            if (!sig.VerifyCertification(secretDSAKey.PublicKey, secretKey.PublicKey))
+            {
+                Fail("subkey binding verification failed.");
+            }
+
+            PgpSignatureSubpacketVector hashedPcks = sig.GetHashedSubPackets();
+            PgpSignatureSubpacketVector unhashedPcks = sig.GetUnhashedSubPackets();
 
-			byte[] sigBytes = sig.GetEncoded();
+            if (hashedPcks.Count != 6)
+            {
+                Fail("wrong number of hashed packets found.");
+            }
 
-			PgpObjectFactory f = new PgpObjectFactory(sigBytes);
+            if (unhashedPcks.Count != 1)
+            {
+                Fail("wrong number of unhashed packets found.");
+            }
 
-			sig = ((PgpSignatureList) f.NextPgpObject())[0];
+            if (!hashedPcks.GetSignerUserId().Equals(TEST_USER_ID))
+            {
+                Fail("test userid not matching");
+            }
 
-			sig.InitVerify(secretDSAKey.PublicKey);
-
-			if (!sig.VerifyCertification(secretDSAKey.PublicKey, secretKey.PublicKey))
-			{
-				Fail("subkey binding verification failed.");
-			}
+            if (hashedPcks.GetSignatureExpirationTime() != TEST_EXPIRATION_TIME)
+            {
+                Fail("test signature expiration time not matching");
+            }
 
-			PgpSignatureSubpacketVector hashedPcks = sig.GetHashedSubPackets();
-			PgpSignatureSubpacketVector unhashedPcks = sig.GetUnhashedSubPackets();
+            if (unhashedPcks.GetIssuerKeyId() != secretDSAKey.KeyId)
+            {
+                Fail("wrong issuer key ID found in certification");
+            }
 
-			if (hashedPcks.Count != 6)
-			{
-				Fail("wrong number of hashed packets found.");
-			}
+            int[] prefAlgs = hashedPcks.GetPreferredCompressionAlgorithms();
+            preferredAlgorithmCheck("compression", PREFERRED_COMPRESSION_ALGORITHMS, prefAlgs);
 
-			if (unhashedPcks.Count != 1)
-			{
-				Fail("wrong number of unhashed packets found.");
-			}
+            prefAlgs = hashedPcks.GetPreferredHashAlgorithms();
+            preferredAlgorithmCheck("hash", PREFERRED_HASH_ALGORITHMS, prefAlgs);
 
-			if (!hashedPcks.GetSignerUserId().Equals(TEST_USER_ID))
-			{
-				Fail("test userid not matching");
-			}
+            prefAlgs = hashedPcks.GetPreferredSymmetricAlgorithms();
+            preferredAlgorithmCheck("symmetric", PREFERRED_SYMMETRIC_ALGORITHMS, prefAlgs);
 
-			if (hashedPcks.GetSignatureExpirationTime() != TEST_EXPIRATION_TIME)
-			{
-				Fail("test signature expiration time not matching");
-			}
+            SignatureSubpacketTag[] criticalHashed = hashedPcks.GetCriticalTags();
 
-			if (unhashedPcks.GetIssuerKeyId() != secretDSAKey.KeyId)
-			{
-				Fail("wrong issuer key ID found in certification");
-			}
+            if (criticalHashed.Length != 1)
+            {
+                Fail("wrong number of critical packets found.");
+            }
 
-			int[] prefAlgs = hashedPcks.GetPreferredCompressionAlgorithms();
-			preferredAlgorithmCheck("compression", PREFERRED_COMPRESSION_ALGORITHMS, prefAlgs);
+            if (criticalHashed[0] != SignatureSubpacketTag.SignerUserId)
+            {
+                Fail("wrong critical packet found in tag list.");
+            }
 
-			prefAlgs = hashedPcks.GetPreferredHashAlgorithms();
-			preferredAlgorithmCheck("hash", PREFERRED_HASH_ALGORITHMS, prefAlgs);
+            //
+            // no packets passed
+            //
+            sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1);
 
-			prefAlgs = hashedPcks.GetPreferredSymmetricAlgorithms();
-			preferredAlgorithmCheck("symmetric", PREFERRED_SYMMETRIC_ALGORITHMS, prefAlgs);
+            sGen.InitSign(PgpSignature.SubkeyBinding, pgpPrivDSAKey);
 
-			SignatureSubpacketTag[] criticalHashed = hashedPcks.GetCriticalTags();
+            sGen.SetHashedSubpackets(null);
+            sGen.SetUnhashedSubpackets(null);
 
-			if (criticalHashed.Length != 1)
-			{
-				Fail("wrong number of critical packets found.");
-			}
+            sig = sGen.GenerateCertification(TEST_USER_ID, secretKey.PublicKey);
 
-			if (criticalHashed[0] != SignatureSubpacketTag.SignerUserId)
-			{
-				Fail("wrong critical packet found in tag list.");
-			}
+            sig.InitVerify(secretDSAKey.PublicKey);
 
-			//
-			// no packets passed
-			//
-			sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1);
+            if (!sig.VerifyCertification(TEST_USER_ID, secretKey.PublicKey))
+            {
+                Fail("subkey binding verification failed.");
+            }
 
-			sGen.InitSign(PgpSignature.SubkeyBinding, pgpPrivDSAKey);
+            hashedPcks = sig.GetHashedSubPackets();
 
-			sGen.SetHashedSubpackets(null);
-			sGen.SetUnhashedSubpackets(null);
+            if (hashedPcks.Count != 1)
+            {
+                Fail("found wrong number of hashed packets");
+            }
 
-			sig = sGen.GenerateCertification(TEST_USER_ID, secretKey.PublicKey);
+            unhashedPcks = sig.GetUnhashedSubPackets();
 
-			sig.InitVerify(secretDSAKey.PublicKey);
+            if (unhashedPcks.Count != 1)
+            {
+                Fail("found wrong number of unhashed packets");
+            }
 
-			if (!sig.VerifyCertification(TEST_USER_ID, secretKey.PublicKey))
-			{
-				Fail("subkey binding verification failed.");
-			}
+            try
+            {
+                sig.VerifyCertification(secretKey.PublicKey);
 
-			hashedPcks = sig.GetHashedSubPackets();
+                Fail("failed to detect non-key signature.");
+            }
+            catch (InvalidOperationException)
+            {
+                // expected
+            }
 
-			if (hashedPcks.Count != 1)
-			{
-				Fail("found wrong number of hashed packets");
-			}
+            //
+            // override hash packets
+            //
+            sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1);
 
-			unhashedPcks = sig.GetUnhashedSubPackets();
+            sGen.InitSign(PgpSignature.SubkeyBinding, pgpPrivDSAKey);
 
-			if (unhashedPcks.Count != 1)
-			{
-				Fail("found wrong number of unhashed packets");
-			}
+            hashedGen = new PgpSignatureSubpacketGenerator();
 
-			try
-			{
-				sig.VerifyCertification(secretKey.PublicKey);
+            DateTime creationTime = new DateTime(1973, 7, 27);
+            hashedGen.SetSignatureCreationTime(false, creationTime);
 
-				Fail("failed to detect non-key signature.");
-			}
-			catch (InvalidOperationException)
-			{
-				// expected
-			}
+            sGen.SetHashedSubpackets(hashedGen.Generate());
 
-			//
-			// override hash packets
-			//
-			sGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1);
+            sGen.SetUnhashedSubpackets(null);
 
-			sGen.InitSign(PgpSignature.SubkeyBinding, pgpPrivDSAKey);
+            sig = sGen.GenerateCertification(TEST_USER_ID, secretKey.PublicKey);
 
-			hashedGen = new PgpSignatureSubpacketGenerator();
+            sig.InitVerify(secretDSAKey.PublicKey);
 
-			DateTime creationTime = new DateTime(1973, 7, 27);
-			hashedGen.SetSignatureCreationTime(false, creationTime);
+            if (!sig.VerifyCertification(TEST_USER_ID, secretKey.PublicKey))
+            {
+                Fail("subkey binding verification failed.");
+            }
 
-			sGen.SetHashedSubpackets(hashedGen.Generate());
+            hashedPcks = sig.GetHashedSubPackets();
+
+            if (hashedPcks.Count != 1)
+            {
+                Fail("found wrong number of hashed packets in override test");
+            }
+
+            if (!hashedPcks.HasSubpacket(SignatureSubpacketTag.CreationTime))
+            {
+                Fail("hasSubpacket test for creation time failed");
+            }
 
-			sGen.SetUnhashedSubpackets(null);
+            DateTime sigCreationTime = hashedPcks.GetSignatureCreationTime();
+            if (!sigCreationTime.Equals(creationTime))
+            {
+                Fail("creation of overridden date failed.");
+            }
 
-			sig = sGen.GenerateCertification(TEST_USER_ID, secretKey.PublicKey);
+            prefAlgs = hashedPcks.GetPreferredCompressionAlgorithms();
+            preferredAlgorithmCheck("compression", NO_PREFERENCES, prefAlgs);
 
-			sig.InitVerify(secretDSAKey.PublicKey);
+            prefAlgs = hashedPcks.GetPreferredHashAlgorithms();
+            preferredAlgorithmCheck("hash", NO_PREFERENCES, prefAlgs);
 
-			if (!sig.VerifyCertification(TEST_USER_ID, secretKey.PublicKey))
-			{
-				Fail("subkey binding verification failed.");
-			}
+            prefAlgs = hashedPcks.GetPreferredSymmetricAlgorithms();
+            preferredAlgorithmCheck("symmetric", NO_PREFERENCES, prefAlgs);
 
-			hashedPcks = sig.GetHashedSubPackets();
-
-			if (hashedPcks.Count != 1)
-			{
-				Fail("found wrong number of hashed packets in override test");
-			}
-
-			if (!hashedPcks.HasSubpacket(SignatureSubpacketTag.CreationTime))
-			{
-				Fail("hasSubpacket test for creation time failed");
-			}
+            if (hashedPcks.GetKeyExpirationTime() != 0)
+            {
+                Fail("unexpected key expiration time found");
+            }
 
-			DateTime sigCreationTime = hashedPcks.GetSignatureCreationTime();
-			if (!sigCreationTime.Equals(creationTime))
-			{
-				Fail("creation of overridden date failed.");
-			}
-
-			prefAlgs = hashedPcks.GetPreferredCompressionAlgorithms();
-			preferredAlgorithmCheck("compression", NO_PREFERENCES, prefAlgs);
-
-			prefAlgs = hashedPcks.GetPreferredHashAlgorithms();
-			preferredAlgorithmCheck("hash", NO_PREFERENCES, prefAlgs);
-
-			prefAlgs = hashedPcks.GetPreferredSymmetricAlgorithms();
-			preferredAlgorithmCheck("symmetric", NO_PREFERENCES, prefAlgs);
-
-			if (hashedPcks.GetKeyExpirationTime() != 0)
-			{
-				Fail("unexpected key expiration time found");
-			}
-
-			if (hashedPcks.GetSignatureExpirationTime() != 0)
-			{
-				Fail("unexpected signature expiration time found");
-			}
-
-			if (hashedPcks.GetSignerUserId() != null)
-			{
-				Fail("unexpected signer user ID found");
-			}
-
-			criticalHashed = hashedPcks.GetCriticalTags();
-
-			if (criticalHashed.Length != 0)
-			{
-				Fail("critical packets found when none expected");
-			}
-
-			unhashedPcks = sig.GetUnhashedSubPackets();
-
-			if (unhashedPcks.Count != 1)
-			{
-				Fail("found wrong number of unhashed packets in override test");
-			}
-
-			//
-			// general signatures
-			//
-			doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha256, secretKey.PublicKey, pgpPrivKey);
-			doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha384, secretKey.PublicKey, pgpPrivKey);
-			doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha512, secretKey.PublicKey, pgpPrivKey);
-			doTestSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-			doTestTextSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
-			doTestTextSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
-			doTestTextSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
-			doTestTextSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
-
-			//
-			// DSA Tests
-			//
-			pgpPriv = new PgpSecretKeyRing(dsaKeyRing);
-			secretKey = pgpPriv.GetSecretKey();
-			pgpPrivKey = secretKey.ExtractPrivateKey(dsaPass);
-
-			try
-			{
-				doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-
-				Fail("DSA wrong key test failed.");
-			}
-			catch (PgpException)
-			{
-				// expected
-			}
-
-			try
-			{
-				doTestSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-
-				Fail("DSA V3 wrong key test failed.");
-			}
-			catch (PgpException)
-			{
-				// expected
-			}
-
-			doTestSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-			doTestSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
-			doTestTextSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
-			doTestTextSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
-			doTestTextSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
-			doTestTextSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
-
-			// special cases
-			//
-			doTestMissingSubpackets(nullPacketsSubKeyBinding);
-
-			doTestMissingSubpackets(generateV3BinarySig(pgpPrivKey, PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1));
-
-			// keyflags
-			doTestKeyFlagsValues();
-		}
-
-		private void doTestKeyFlagsValues()
-		{
-			checkValue(KeyFlags.CertifyOther, 0x01);
-			checkValue(KeyFlags.SignData, 0x02);
-			checkValue(KeyFlags.EncryptComms, 0x04);
-			checkValue(KeyFlags.EncryptStorage, 0x08);
-			checkValue(KeyFlags.Split, 0x10);
-			checkValue(KeyFlags.Authentication, 0x20);
-			checkValue(KeyFlags.Shared, 0x80);
-
-			// yes this actually happens
-			checkValue(new byte[] { 4, 0, 0, 0 }, 0x04);
-			checkValue(new byte[] { 4, 0, 0 }, 0x04);
-			checkValue(new byte[] { 4, 0 }, 0x04);
-			checkValue(new byte[] { 4 }, 0x04);
-		}
-
-		private void checkValue(int flag, int val)
-		{
-			KeyFlags f = new KeyFlags(true, flag);
-
-			if (f.Flags != val)
-			{
-				Fail("flag value mismatch");
-			}
-		}
-
-		private void checkValue(byte[] flag, int val)
-		{
-			KeyFlags f = new KeyFlags(true, flag);
-
-			if (f.Flags != val)
-			{
-				Fail("flag value mismatch");
-			}
-		}
-
-		private void doTestMissingSubpackets(byte[] signature)
-		{
-			PgpObjectFactory f = new PgpObjectFactory(signature);
-			object obj = f.NextPgpObject();
-
-			while (!(obj is PgpSignatureList))
-			{
-				obj = f.NextPgpObject();
-				if (obj is PgpLiteralData)
-				{
-					Stream input = ((PgpLiteralData)obj).GetDataStream();
-					Streams.Drain(input);
-				}
-			}
-
-			PgpSignature sig = ((PgpSignatureList)obj)[0];
-
-			if (sig.Version > 3)
-			{
-				PgpSignatureSubpacketVector v = sig.GetHashedSubPackets();
-
-				if (v.GetKeyExpirationTime() != 0)
-				{
-					Fail("key expiration time not zero for missing subpackets");
-				}
-
-				if (!sig.HasSubpackets)
-				{
-					Fail("HasSubpackets property was false with packets");
-				}
-			}
-			else
-			{
-				if (sig.GetHashedSubPackets() != null)
-				{
-					Fail("hashed sub packets found when none expected");
-				}
-
-				if (sig.GetUnhashedSubPackets() != null)
-				{
-					Fail("unhashed sub packets found when none expected");
-				}
-
-				if (sig.HasSubpackets)
-				{
-					Fail("HasSubpackets property was true with no packets");
-				}
-			}
-		}
-
-		private void preferredAlgorithmCheck(
-			string	type,
-			int[]	expected,
-			int[]	prefAlgs)
-		{
-			if (expected == null)
-			{
-				if (prefAlgs != null)
-				{
-					Fail("preferences for " + type + " found when none expected");
-				}
-			}
-			else
-			{
-				if (prefAlgs.Length != expected.Length)
-				{
-					Fail("wrong number of preferred " + type + " algorithms found");
-				}
-
-				for (int i = 0; i != expected.Length; i++)
-				{
-					if (expected[i] != prefAlgs[i])
-					{
-						Fail("wrong algorithm found for " + type + ": expected " + expected[i] + " got " + prefAlgs);
-					}
-				}
-			}
-		}
-
-		private void doTestSig(
-			PublicKeyAlgorithmTag	encAlgorithm,
-			HashAlgorithmTag		hashAlgorithm,
-			PgpPublicKey			pubKey,
-			PgpPrivateKey			privKey)
-		{
-			MemoryStream bOut = new MemoryStream();
-			MemoryStream testIn = new MemoryStream(TEST_DATA, false);
-			PgpSignatureGenerator sGen = new PgpSignatureGenerator(encAlgorithm, hashAlgorithm);
-
-			sGen.InitSign(PgpSignature.BinaryDocument, privKey);
-			sGen.GenerateOnePassVersion(false).Encode(bOut);
-
-			PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
-			Stream lOut = lGen.Open(
-				new UncloseableStream(bOut),
-				PgpLiteralData.Binary,
-				"_CONSOLE",
-				TEST_DATA.Length * 2,
-				DateTime.UtcNow);
-
-			int ch;
-			while ((ch = testIn.ReadByte()) >= 0)
-			{
-				lOut.WriteByte((byte)ch);
-				sGen.Update((byte)ch);
-			}
-
-			lOut.Write(TEST_DATA, 0, TEST_DATA.Length);
-			sGen.Update(TEST_DATA);
-
-			lGen.Close();
-
-			sGen.Generate().Encode(bOut);
-
-			verifySignature(bOut.ToArray(), hashAlgorithm, pubKey, TEST_DATA);
-		}
-
-		private void doTestTextSig(
-			PublicKeyAlgorithmTag	encAlgorithm,
-			HashAlgorithmTag		hashAlgorithm,
-			PgpPublicKey			pubKey,
-			PgpPrivateKey			privKey,
-			byte[]					data,
-			byte[]					canonicalData)
-		{
-			PgpSignatureGenerator sGen = new PgpSignatureGenerator(encAlgorithm, HashAlgorithmTag.Sha1);
-			MemoryStream bOut = new MemoryStream();
-			MemoryStream testIn = new MemoryStream(data, false);
-			DateTime creationTime = DateTime.UtcNow;
-
-			sGen.InitSign(PgpSignature.CanonicalTextDocument, privKey);
-			sGen.GenerateOnePassVersion(false).Encode(bOut);
-
-			PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
-			Stream lOut = lGen.Open(
-				new UncloseableStream(bOut),
-				PgpLiteralData.Text,
-				"_CONSOLE",
-				data.Length * 2,
-				creationTime);
-
-			int ch;
-			while ((ch = testIn.ReadByte()) >= 0)
-			{
-				lOut.WriteByte((byte)ch);
-				sGen.Update((byte)ch);
-			}
-
-			lOut.Write(data, 0, data.Length);
-			sGen.Update(data);
-
-			lGen.Close();
-
-			PgpSignature sig = sGen.Generate();
-
-			if (sig.CreationTime == DateTimeUtilities.UnixMsToDateTime(0))
-			{
-				Fail("creation time not set in v4 signature");
-			}
-
-			sig.Encode(bOut);
-
-			verifySignature(bOut.ToArray(), hashAlgorithm, pubKey, canonicalData);
-		}
-
-		private void doTestSigV3(
-			PublicKeyAlgorithmTag	encAlgorithm,
-			HashAlgorithmTag		hashAlgorithm,
-			PgpPublicKey			pubKey,
-			PgpPrivateKey			privKey)
-		{
-			byte[] bytes = generateV3BinarySig(privKey, encAlgorithm, hashAlgorithm);
-
-			verifySignature(bytes, hashAlgorithm, pubKey, TEST_DATA);
-		}
-
-		private byte[] generateV3BinarySig(
-			PgpPrivateKey			privKey,
-			PublicKeyAlgorithmTag	encAlgorithm,
-			HashAlgorithmTag		hashAlgorithm)
-		{
-			MemoryStream bOut = new MemoryStream();
-			MemoryStream testIn = new MemoryStream(TEST_DATA, false);
-			PgpV3SignatureGenerator sGen = new PgpV3SignatureGenerator(encAlgorithm, hashAlgorithm);
-
-			sGen.InitSign(PgpSignature.BinaryDocument, privKey);
-			sGen.GenerateOnePassVersion(false).Encode(bOut);
-
-			PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
-			Stream lOut = lGen.Open(
-				new UncloseableStream(bOut),
-				PgpLiteralData.Binary,
-				"_CONSOLE",
-				TEST_DATA.Length * 2,
-				DateTime.UtcNow);
-
-			int ch;
-			while ((ch = testIn.ReadByte()) >= 0)
-			{
-				lOut.WriteByte((byte)ch);
-				sGen.Update((byte)ch);
-			}
-
-			lOut.Write(TEST_DATA, 0, TEST_DATA.Length);
-			sGen.Update(TEST_DATA);
-
-			lGen.Close();
-
-			sGen.Generate().Encode(bOut);
-
-			return bOut.ToArray();
-		}
-
-		private void doTestTextSigV3(
-			PublicKeyAlgorithmTag	encAlgorithm,
-			HashAlgorithmTag		hashAlgorithm,
-			PgpPublicKey			pubKey,
-			PgpPrivateKey			privKey,
-			byte[]					data,
-			byte[]					canonicalData)
-		{
-			PgpV3SignatureGenerator sGen = new PgpV3SignatureGenerator(encAlgorithm, HashAlgorithmTag.Sha1);
-			MemoryStream bOut = new MemoryStream();
-			MemoryStream testIn = new MemoryStream(data, false);
-
-			sGen.InitSign(PgpSignature.CanonicalTextDocument, privKey);
-			sGen.GenerateOnePassVersion(false).Encode(bOut);
-
-			PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
-			Stream lOut = lGen.Open(
-				new UncloseableStream(bOut),
-				PgpLiteralData.Text,
-				"_CONSOLE",
-				data.Length * 2,
-				DateTime.UtcNow);
-
-			int ch;
-			while ((ch = testIn.ReadByte()) >= 0)
-			{
-				lOut.WriteByte((byte)ch);
-				sGen.Update((byte)ch);
-			}
-
-			lOut.Write(data, 0, data.Length);
-			sGen.Update(data);
-
-			lGen.Close();
-
-			PgpSignature sig = sGen.Generate();
-
-			if (sig.CreationTime == DateTimeUtilities.UnixMsToDateTime(0))
-			{
-				Fail("creation time not set in v3 signature");
-			}
-
-			sig.Encode(bOut);
-
-			verifySignature(bOut.ToArray(), hashAlgorithm, pubKey, canonicalData);
-		}
-
-		private void verifySignature(
-			byte[] encodedSig,
-			HashAlgorithmTag hashAlgorithm,
-			PgpPublicKey pubKey,
-			byte[] original)
-		{
-			PgpObjectFactory        pgpFact = new PgpObjectFactory(encodedSig);
-			PgpOnePassSignatureList p1 = (PgpOnePassSignatureList)pgpFact.NextPgpObject();
-			PgpOnePassSignature     ops = p1[0];
-			PgpLiteralData          p2 = (PgpLiteralData)pgpFact.NextPgpObject();
-			Stream					dIn = p2.GetInputStream();
-
-			ops.InitVerify(pubKey);
-
-			int ch;
-			while ((ch = dIn.ReadByte()) >= 0)
-			{
-				ops.Update((byte)ch);
-			}
-
-			PgpSignatureList p3 = (PgpSignatureList)pgpFact.NextPgpObject();
-			PgpSignature sig = p3[0];
-
-			DateTime creationTime = sig.CreationTime;
-
-			// Check creationTime is recent
-			if (creationTime.CompareTo(DateTime.UtcNow) > 0
-				|| creationTime.CompareTo(DateTime.UtcNow.AddMinutes(-10)) < 0)
-			{
-				Fail("bad creation time in signature: " + creationTime);
-			}
-
-			if (sig.KeyId != pubKey.KeyId)
-			{
-				Fail("key id mismatch in signature");
-			}
-
-			if (!ops.Verify(sig))
-			{
-				Fail("Failed generated signature check - " + hashAlgorithm);
-			}
-
-			sig.InitVerify(pubKey);
-
-			for (int i = 0; i != original.Length; i++)
-			{
-				sig.Update(original[i]);
-			}
-
-			sig.Update(original);
-
-			if (!sig.Verify())
-			{
-				Fail("Failed generated signature check against original data");
-			}
-		}
-
-		public override string Name
-		{
-			get { return "PGPSignatureTest"; }
-		}
-
-		public static void Main(
-			string[] args)
-		{
-			RunTest(new PgpSignatureTest());
-		}
-
-		[Test]
-		public void TestFunction()
-		{
-			string resultText = Perform().ToString();
-
-			Assert.AreEqual(Name + ": Okay", resultText);
-		}
-	}
+            if (hashedPcks.GetSignatureExpirationTime() != 0)
+            {
+                Fail("unexpected signature expiration time found");
+            }
+
+            if (hashedPcks.GetSignerUserId() != null)
+            {
+                Fail("unexpected signer user ID found");
+            }
+
+            criticalHashed = hashedPcks.GetCriticalTags();
+
+            if (criticalHashed.Length != 0)
+            {
+                Fail("critical packets found when none expected");
+            }
+
+            unhashedPcks = sig.GetUnhashedSubPackets();
+
+            if (unhashedPcks.Count != 1)
+            {
+                Fail("found wrong number of unhashed packets in override test");
+            }
+
+            //
+            // general signatures
+            //
+            doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha256, secretKey.PublicKey, pgpPrivKey);
+            doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha384, secretKey.PublicKey, pgpPrivKey);
+            doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha512, secretKey.PublicKey, pgpPrivKey);
+            doTestSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+            doTestTextSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
+            doTestTextSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
+            doTestTextSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
+            doTestTextSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
+
+            //
+            // DSA Tests
+            //
+            pgpPriv = new PgpSecretKeyRing(dsaKeyRing);
+            secretKey = pgpPriv.GetSecretKey();
+            pgpPrivKey = secretKey.ExtractPrivateKey(dsaPass);
+
+            try
+            {
+                doTestSig(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+
+                Fail("DSA wrong key test failed.");
+            }
+            catch (PgpException)
+            {
+                // expected
+            }
+
+            try
+            {
+                doTestSigV3(PublicKeyAlgorithmTag.RsaGeneral, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+
+                Fail("DSA V3 wrong key test failed.");
+            }
+            catch (PgpException)
+            {
+                // expected
+            }
+
+            doTestSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+            doTestSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey);
+            doTestTextSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
+            doTestTextSig(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
+            doTestTextSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF);
+            doTestTextSigV3(PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1, secretKey.PublicKey, pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF);
+
+            // special cases
+            //
+            doTestMissingSubpackets(nullPacketsSubKeyBinding);
+
+            doTestMissingSubpackets(generateV3BinarySig(pgpPrivKey, PublicKeyAlgorithmTag.Dsa, HashAlgorithmTag.Sha1));
+
+            // keyflags
+            doTestKeyFlagsValues();
+
+            // TODO Seems to depend on some other functionality that's yet to be ported
+            //doTestUserAttributeEncoding();
+        }
+
+        //private void doTestUserAttributeEncoding()
+        //{
+        //    PgpPublicKeyRing pkr = new PgpPublicKeyRing(okAttr);
+
+        //    CheckUserAttribute("normal", pkr, pkr.GetPublicKey());
+
+        //    pkr = new PgpPublicKeyRing(attrLongLength);
+
+        //    CheckUserAttribute("long", pkr, pkr.GetPublicKey());
+        //}
+
+        //private void CheckUserAttribute(String type, PgpPublicKeyRing pkr, PgpPublicKey masterPk)
+        //{
+        //    foreach (PgpUserAttributeSubpacketVector attr in pkr.GetPublicKey().GetUserAttributes())
+        //    {
+        //        foreach (PgpSignature sig in masterPk.GetSignaturesForUserAttribute(attr))
+        //        {
+        //            sig.InitVerify(masterPk);
+        //            if (!sig.VerifyCertification(attr, masterPk))
+        //            {
+        //                Fail("user attribute sig failed to verify on " + type);
+        //            }
+        //        }
+        //    }
+        //}
+
+        private void doTestKeyFlagsValues()
+        {
+            checkValue(KeyFlags.CertifyOther, 0x01);
+            checkValue(KeyFlags.SignData, 0x02);
+            checkValue(KeyFlags.EncryptComms, 0x04);
+            checkValue(KeyFlags.EncryptStorage, 0x08);
+            checkValue(KeyFlags.Split, 0x10);
+            checkValue(KeyFlags.Authentication, 0x20);
+            checkValue(KeyFlags.Shared, 0x80);
+
+            // yes this actually happens
+            checkValue(new byte[] { 4, 0, 0, 0 }, 0x04);
+            checkValue(new byte[] { 4, 0, 0 }, 0x04);
+            checkValue(new byte[] { 4, 0 }, 0x04);
+            checkValue(new byte[] { 4 }, 0x04);
+        }
+
+        private void checkValue(int flag, int val)
+        {
+            KeyFlags f = new KeyFlags(true, flag);
+
+            if (f.Flags != val)
+            {
+                Fail("flag value mismatch");
+            }
+        }
+
+        private void checkValue(byte[] flag, int val)
+        {
+            KeyFlags f = new KeyFlags(true, false, flag);
+
+            if (f.Flags != val)
+            {
+                Fail("flag value mismatch");
+            }
+        }
+
+        private void doTestMissingSubpackets(byte[] signature)
+        {
+            PgpObjectFactory f = new PgpObjectFactory(signature);
+            object obj = f.NextPgpObject();
+
+            while (!(obj is PgpSignatureList))
+            {
+                obj = f.NextPgpObject();
+                if (obj is PgpLiteralData)
+                {
+                    Stream input = ((PgpLiteralData)obj).GetDataStream();
+                    Streams.Drain(input);
+                }
+            }
+
+            PgpSignature sig = ((PgpSignatureList)obj)[0];
+
+            if (sig.Version > 3)
+            {
+                PgpSignatureSubpacketVector v = sig.GetHashedSubPackets();
+
+                if (v.GetKeyExpirationTime() != 0)
+                {
+                    Fail("key expiration time not zero for missing subpackets");
+                }
+
+                if (!sig.HasSubpackets)
+                {
+                    Fail("HasSubpackets property was false with packets");
+                }
+            }
+            else
+            {
+                if (sig.GetHashedSubPackets() != null)
+                {
+                    Fail("hashed sub packets found when none expected");
+                }
+
+                if (sig.GetUnhashedSubPackets() != null)
+                {
+                    Fail("unhashed sub packets found when none expected");
+                }
+
+                if (sig.HasSubpackets)
+                {
+                    Fail("HasSubpackets property was true with no packets");
+                }
+            }
+        }
+
+        private void preferredAlgorithmCheck(
+            string	type,
+            int[]	expected,
+            int[]	prefAlgs)
+        {
+            if (expected == null)
+            {
+                if (prefAlgs != null)
+                {
+                    Fail("preferences for " + type + " found when none expected");
+                }
+            }
+            else
+            {
+                if (prefAlgs.Length != expected.Length)
+                {
+                    Fail("wrong number of preferred " + type + " algorithms found");
+                }
+
+                for (int i = 0; i != expected.Length; i++)
+                {
+                    if (expected[i] != prefAlgs[i])
+                    {
+                        Fail("wrong algorithm found for " + type + ": expected " + expected[i] + " got " + prefAlgs);
+                    }
+                }
+            }
+        }
+
+        private void doTestSig(
+            PublicKeyAlgorithmTag	encAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            PgpPublicKey			pubKey,
+            PgpPrivateKey			privKey)
+        {
+            MemoryStream bOut = new MemoryStream();
+            MemoryStream testIn = new MemoryStream(TEST_DATA, false);
+            PgpSignatureGenerator sGen = new PgpSignatureGenerator(encAlgorithm, hashAlgorithm);
+
+            sGen.InitSign(PgpSignature.BinaryDocument, privKey);
+            sGen.GenerateOnePassVersion(false).Encode(bOut);
+
+            PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
+            Stream lOut = lGen.Open(
+                new UncloseableStream(bOut),
+                PgpLiteralData.Binary,
+                "_CONSOLE",
+                TEST_DATA.Length * 2,
+                DateTime.UtcNow);
+
+            int ch;
+            while ((ch = testIn.ReadByte()) >= 0)
+            {
+                lOut.WriteByte((byte)ch);
+                sGen.Update((byte)ch);
+            }
+
+            lOut.Write(TEST_DATA, 0, TEST_DATA.Length);
+            sGen.Update(TEST_DATA);
+
+            lGen.Close();
+
+            sGen.Generate().Encode(bOut);
+
+            verifySignature(bOut.ToArray(), hashAlgorithm, pubKey, TEST_DATA);
+        }
+
+        private void doTestTextSig(
+            PublicKeyAlgorithmTag	encAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            PgpPublicKey			pubKey,
+            PgpPrivateKey			privKey,
+            byte[]					data,
+            byte[]					canonicalData)
+        {
+            PgpSignatureGenerator sGen = new PgpSignatureGenerator(encAlgorithm, HashAlgorithmTag.Sha1);
+            MemoryStream bOut = new MemoryStream();
+            MemoryStream testIn = new MemoryStream(data, false);
+            DateTime creationTime = DateTime.UtcNow;
+
+            sGen.InitSign(PgpSignature.CanonicalTextDocument, privKey);
+            sGen.GenerateOnePassVersion(false).Encode(bOut);
+
+            PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
+            Stream lOut = lGen.Open(
+                new UncloseableStream(bOut),
+                PgpLiteralData.Text,
+                "_CONSOLE",
+                data.Length * 2,
+                creationTime);
+
+            int ch;
+            while ((ch = testIn.ReadByte()) >= 0)
+            {
+                lOut.WriteByte((byte)ch);
+                sGen.Update((byte)ch);
+            }
+
+            lOut.Write(data, 0, data.Length);
+            sGen.Update(data);
+
+            lGen.Close();
+
+            PgpSignature sig = sGen.Generate();
+
+            if (sig.CreationTime == DateTimeUtilities.UnixMsToDateTime(0))
+            {
+                Fail("creation time not set in v4 signature");
+            }
+
+            sig.Encode(bOut);
+
+            verifySignature(bOut.ToArray(), hashAlgorithm, pubKey, canonicalData);
+        }
+
+        private void doTestSigV3(
+            PublicKeyAlgorithmTag	encAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            PgpPublicKey			pubKey,
+            PgpPrivateKey			privKey)
+        {
+            byte[] bytes = generateV3BinarySig(privKey, encAlgorithm, hashAlgorithm);
+
+            verifySignature(bytes, hashAlgorithm, pubKey, TEST_DATA);
+        }
+
+        private byte[] generateV3BinarySig(
+            PgpPrivateKey			privKey,
+            PublicKeyAlgorithmTag	encAlgorithm,
+            HashAlgorithmTag		hashAlgorithm)
+        {
+            MemoryStream bOut = new MemoryStream();
+            MemoryStream testIn = new MemoryStream(TEST_DATA, false);
+            PgpV3SignatureGenerator sGen = new PgpV3SignatureGenerator(encAlgorithm, hashAlgorithm);
+
+            sGen.InitSign(PgpSignature.BinaryDocument, privKey);
+            sGen.GenerateOnePassVersion(false).Encode(bOut);
+
+            PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
+            Stream lOut = lGen.Open(
+                new UncloseableStream(bOut),
+                PgpLiteralData.Binary,
+                "_CONSOLE",
+                TEST_DATA.Length * 2,
+                DateTime.UtcNow);
+
+            int ch;
+            while ((ch = testIn.ReadByte()) >= 0)
+            {
+                lOut.WriteByte((byte)ch);
+                sGen.Update((byte)ch);
+            }
+
+            lOut.Write(TEST_DATA, 0, TEST_DATA.Length);
+            sGen.Update(TEST_DATA);
+
+            lGen.Close();
+
+            sGen.Generate().Encode(bOut);
+
+            return bOut.ToArray();
+        }
+
+        private void doTestTextSigV3(
+            PublicKeyAlgorithmTag	encAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            PgpPublicKey			pubKey,
+            PgpPrivateKey			privKey,
+            byte[]					data,
+            byte[]					canonicalData)
+        {
+            PgpV3SignatureGenerator sGen = new PgpV3SignatureGenerator(encAlgorithm, HashAlgorithmTag.Sha1);
+            MemoryStream bOut = new MemoryStream();
+            MemoryStream testIn = new MemoryStream(data, false);
+
+            sGen.InitSign(PgpSignature.CanonicalTextDocument, privKey);
+            sGen.GenerateOnePassVersion(false).Encode(bOut);
+
+            PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
+            Stream lOut = lGen.Open(
+                new UncloseableStream(bOut),
+                PgpLiteralData.Text,
+                "_CONSOLE",
+                data.Length * 2,
+                DateTime.UtcNow);
+
+            int ch;
+            while ((ch = testIn.ReadByte()) >= 0)
+            {
+                lOut.WriteByte((byte)ch);
+                sGen.Update((byte)ch);
+            }
+
+            lOut.Write(data, 0, data.Length);
+            sGen.Update(data);
+
+            lGen.Close();
+
+            PgpSignature sig = sGen.Generate();
+
+            if (sig.CreationTime == DateTimeUtilities.UnixMsToDateTime(0))
+            {
+                Fail("creation time not set in v3 signature");
+            }
+
+            sig.Encode(bOut);
+
+            verifySignature(bOut.ToArray(), hashAlgorithm, pubKey, canonicalData);
+        }
+
+        private void verifySignature(
+            byte[] encodedSig,
+            HashAlgorithmTag hashAlgorithm,
+            PgpPublicKey pubKey,
+            byte[] original)
+        {
+            PgpObjectFactory        pgpFact = new PgpObjectFactory(encodedSig);
+            PgpOnePassSignatureList p1 = (PgpOnePassSignatureList)pgpFact.NextPgpObject();
+            PgpOnePassSignature     ops = p1[0];
+            PgpLiteralData          p2 = (PgpLiteralData)pgpFact.NextPgpObject();
+            Stream					dIn = p2.GetInputStream();
+
+            ops.InitVerify(pubKey);
+
+            int ch;
+            while ((ch = dIn.ReadByte()) >= 0)
+            {
+                ops.Update((byte)ch);
+            }
+
+            PgpSignatureList p3 = (PgpSignatureList)pgpFact.NextPgpObject();
+            PgpSignature sig = p3[0];
+
+            DateTime creationTime = sig.CreationTime;
+
+            // Check creationTime is recent
+            if (creationTime.CompareTo(DateTime.UtcNow) > 0
+                || creationTime.CompareTo(DateTime.UtcNow.AddMinutes(-10)) < 0)
+            {
+                Fail("bad creation time in signature: " + creationTime);
+            }
+
+            if (sig.KeyId != pubKey.KeyId)
+            {
+                Fail("key id mismatch in signature");
+            }
+
+            if (!ops.Verify(sig))
+            {
+                Fail("Failed generated signature check - " + hashAlgorithm);
+            }
+
+            sig.InitVerify(pubKey);
+
+            for (int i = 0; i != original.Length; i++)
+            {
+                sig.Update(original[i]);
+            }
+
+            sig.Update(original);
+
+            if (!sig.Verify())
+            {
+                Fail("Failed generated signature check against original data");
+            }
+        }
+
+        public override string Name
+        {
+            get { return "PGPSignatureTest"; }
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new PgpSignatureTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
 }
diff --git a/crypto/test/src/openpgp/test/PgpECDHTest.cs b/crypto/test/src/openpgp/test/PgpECDHTest.cs
new file mode 100644
index 000000000..b7c500bd0
--- /dev/null
+++ b/crypto/test/src/openpgp/test/PgpECDHTest.cs
@@ -0,0 +1,280 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
+{
+    [TestFixture]
+    public class PgpECDHTest
+        : SimpleTest
+    {
+        private static readonly byte[] testPubKey =
+            Base64.Decode(
+                "mFIEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" +
+                "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKstFBUZXN0IEVDRFNB" +
+                "LUVDREggKEtleSBhbmQgc3Via2V5IGFyZSAyNTYgYml0cyBsb25nKSA8dGVzdC5l" +
+                "Y2RzYS5lY2RoQGV4YW1wbGUuY29tPoh6BBMTCAAiBQJRvgbAAhsDBgsJCAcDAgYV" +
+                "CAIJCgsEFgIDAQIeAQIXgAAKCRD3wDlWjFo9U5O2AQDi89NO6JbaIObC63jMMWsi" +
+                "AaQHrBCPkDZLibgNv73DLgD/faouH4YZJs+cONQBPVnP1baG1NpWR5ppN3JULFcr" +
+                "hcq4VgRRvgbAEggqhkjOPQMBBwIDBLtY8Nmfz0zSEa8C1snTOWN+VcT8pXPwgJRy" +
+                "z6kSP4nPt1xj1lPKj5zwPXKWxMkPO9ocqhKdg2mOh6/rc1ObIoMDAQgHiGEEGBMI" +
+                "AAkFAlG+BsACGwwACgkQ98A5VoxaPVN8cgEAj4dMNMNwRSg2ZBWunqUAHqIedVbS" +
+                "dmwmbysD192L3z4A/ReXEa0gtv8OFWjuALD1ovEK8TpDORLUb6IuUb5jUIzY");
+
+        private static readonly byte[] testPrivKey =
+            Base64.Decode(
+                "lKUEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" +
+                "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKs/gcDAo11YYCae/K2" +
+                "1uKGJ/uU4b4QHYnPIsAdYpuo5HIdoAOL/WwduRa8C6vSFrtMJLDqPK3BUpMz3CXN" +
+                "GyMhjuaHKP5MPbBZkIfgUGZO5qvU9+i0UFRlc3QgRUNEU0EtRUNESCAoS2V5IGFu" +
+                "ZCBzdWJrZXkgYXJlIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhLmVjZGhAZXhh" +
+                "bXBsZS5jb20+iHoEExMIACIFAlG+BsACGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B" +
+                "AheAAAoJEPfAOVaMWj1Tk7YBAOLz007oltog5sLreMwxayIBpAesEI+QNkuJuA2/" +
+                "vcMuAP99qi4fhhkmz5w41AE9Wc/VtobU2lZHmmk3clQsVyuFyg==");
+
+        private static readonly byte[] testMessage =
+            Base64.Decode(
+                "hH4Dp5+FdoujIBwSAgMErx4BSvgXY3irwthgxU8zPoAoR+8rhmxdpwbw6ZJAO2GX" +
+                "azWJ85JNcobHKDeGeUq6wkTFu+g6yG99gIX8J5xJAjBRhyCRcaFgwbdDV4orWTe3" +
+                "iewiT8qs4BQ23e0c8t+thdKoK4thMsCJy7wSKqY0sJTSVAELroNbCOi2lcO15YmW" +
+                "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" +
+                "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg==");
+
+        private void Generate()
+        {
+            SecureRandom random = SecureRandom.GetInstance("SHA1PRNG");
+
+            //
+            // Generate a master key
+            //
+            IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
+            keyGen.Init(new ECKeyGenerationParameters(SecObjectIdentifiers.SecP256r1, random));
+
+            AsymmetricCipherKeyPair kpSign = keyGen.GenerateKeyPair();
+
+            PgpKeyPair ecdsaKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.ECDsa, kpSign, DateTime.UtcNow);
+
+            //
+            // Generate an encryption key
+            //
+            keyGen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
+            keyGen.Init(new ECKeyGenerationParameters(SecObjectIdentifiers.SecP256r1, random));
+
+            AsymmetricCipherKeyPair kpEnc = keyGen.GenerateKeyPair();
+
+            PgpKeyPair ecdhKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.ECDH, kpEnc, DateTime.UtcNow);
+
+            //
+            // Generate a key ring
+            //
+            char[] passPhrase = "test".ToCharArray();
+            PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator(PgpSignature.PositiveCertification, ecdsaKeyPair,
+                "test@bouncycastle.org", SymmetricKeyAlgorithmTag.Aes256, passPhrase, true, null, null, random);
+            keyRingGen.AddSubKey(ecdhKeyPair);
+
+            PgpPublicKeyRing pubRing = keyRingGen.GeneratePublicKeyRing();
+
+            // TODO: add check of KdfParameters
+            DoBasicKeyRingCheck(pubRing);
+
+            PgpSecretKeyRing secRing = keyRingGen.GenerateSecretKeyRing();
+
+            PgpPublicKeyRing pubRingEnc = new PgpPublicKeyRing(pubRing.GetEncoded());
+            if (!Arrays.AreEqual(pubRing.GetEncoded(), pubRingEnc.GetEncoded()))
+            {
+                Fail("public key ring encoding failed");
+            }
+
+            PgpSecretKeyRing secRingEnc = new PgpSecretKeyRing(secRing.GetEncoded());
+            if (!Arrays.AreEqual(secRing.GetEncoded(), secRingEnc.GetEncoded()))
+            {
+                Fail("secret key ring encoding failed");
+            }
+
+            PgpPrivateKey pgpPrivKey = secRing.GetSecretKey().ExtractPrivateKey(passPhrase);
+        }
+
+        private void TestDecrypt(PgpSecretKeyRing secretKeyRing)
+        {
+            PgpObjectFactory pgpF = new PgpObjectFactory(testMessage);
+
+            PgpEncryptedDataList encList = (PgpEncryptedDataList)pgpF.NextPgpObject();
+
+            PgpPublicKeyEncryptedData encP = (PgpPublicKeyEncryptedData)encList[0];
+
+            PgpSecretKey secretKey = secretKeyRing.GetSecretKey(); // secretKeyRing.GetSecretKey(encP.KeyId);
+
+    //        PgpPrivateKey pgpPrivKey = secretKey.extractPrivateKey(new JcePBESecretKeyEncryptorBuilder());
+
+    //        clear = encP.getDataStream(pgpPrivKey, "BC");
+    //
+    //        bOut.reset();
+    //
+    //        while ((ch = clear.read()) >= 0)
+    //        {
+    //            bOut.write(ch);
+    //        }
+    //
+    //        out = bOut.toByteArray();
+    //
+    //        if (!AreEqual(out, text))
+    //        {
+    //            fail("wrong plain text in Generated packet");
+    //        }
+        }
+
+        private void EncryptDecryptTest()
+        {
+            SecureRandom random = SecureRandom.GetInstance("SHA1PRNG");
+
+            byte[] text = Encoding.ASCII.GetBytes("hello world!");
+
+            IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
+            keyGen.Init(new ECKeyGenerationParameters(SecObjectIdentifiers.SecP256r1, random));
+
+            AsymmetricCipherKeyPair kpEnc = keyGen.GenerateKeyPair();
+
+            PgpKeyPair ecdhKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.ECDH, kpEnc, DateTime.UtcNow);
+
+            PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator();
+            MemoryStream ldOut = new MemoryStream();
+            Stream pOut = lData.Open(ldOut, PgpLiteralDataGenerator.Utf8, PgpLiteralData.Console, text.Length, DateTime.UtcNow);
+
+            pOut.Write(text, 0, text.Length);
+
+            pOut.Close();
+
+            byte[] data = ldOut.ToArray();
+
+            MemoryStream cbOut = new MemoryStream();
+
+            PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, random);
+            cPk.AddMethod(ecdhKeyPair.PublicKey);
+
+            Stream cOut = cPk.Open(new UncloseableStream(cbOut), data.Length);
+
+            cOut.Write(data, 0, data.Length);
+
+            cOut.Close();
+
+            PgpObjectFactory pgpF = new PgpObjectFactory(cbOut.ToArray());
+
+            PgpEncryptedDataList encList = (PgpEncryptedDataList)pgpF.NextPgpObject();
+
+            PgpPublicKeyEncryptedData encP = (PgpPublicKeyEncryptedData)encList[0];
+
+            Stream clear = encP.GetDataStream(ecdhKeyPair.PrivateKey);
+
+            pgpF = new PgpObjectFactory(clear);
+
+            PgpLiteralData ld = (PgpLiteralData)pgpF.NextPgpObject();
+
+            clear = ld.GetInputStream();
+            MemoryStream bOut = new MemoryStream();
+
+            int ch;
+            while ((ch = clear.ReadByte()) >= 0)
+            {
+                bOut.WriteByte((byte)ch);
+            }
+
+            byte[] output = bOut.ToArray();
+
+            if (!AreEqual(output, text))
+            {
+                Fail("wrong plain text in Generated packet");
+            }
+        }
+
+        public override void PerformTest()
+        {
+            //
+            // Read the public key
+            //
+            PgpPublicKeyRing pubKeyRing = new PgpPublicKeyRing(testPubKey);
+
+            DoBasicKeyRingCheck(pubKeyRing);
+
+            //
+            // Read the private key
+            //
+            PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing(testPrivKey);
+
+            TestDecrypt(secretKeyRing);
+
+            EncryptDecryptTest();
+
+            Generate();
+        }
+
+        private void DoBasicKeyRingCheck(PgpPublicKeyRing pubKeyRing)
+        {
+            foreach (PgpPublicKey pubKey in pubKeyRing.GetPublicKeys())
+            {
+                if (pubKey.IsMasterKey)
+                {
+                    if (pubKey.IsEncryptionKey)
+                    {
+                        Fail("master key showed as encryption key!");
+                    }
+                }
+                else
+                {
+                    if (!pubKey.IsEncryptionKey)
+                    {
+                        Fail("sub key not encryption key!");
+                    }
+
+                    foreach (PgpSignature certification in pubKeyRing.GetPublicKey().GetSignatures())
+                    {
+                        certification.InitVerify(pubKeyRing.GetPublicKey());
+
+                        if (!certification.VerifyCertification((string)First(pubKeyRing.GetPublicKey().GetUserIds()), pubKeyRing.GetPublicKey()))
+                        {
+                            Fail("subkey certification does not verify");
+                        }
+                    }
+                }
+            }
+        }
+
+        private static object First(IEnumerable e)
+        {
+            IEnumerator n = e.GetEnumerator();
+            Assert.IsTrue(n.MoveNext());
+            return n.Current;
+        }
+
+        public override string Name
+        {
+            get { return "PgpECDHTest"; }
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new PgpECDHTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/openpgp/test/PgpECDsaTest.cs b/crypto/test/src/openpgp/test/PgpECDsaTest.cs
new file mode 100644
index 000000000..6259ef627
--- /dev/null
+++ b/crypto/test/src/openpgp/test/PgpECDsaTest.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
+{
+    [TestFixture]
+    public class PgpECDsaTest
+        : SimpleTest
+    {
+        private static readonly byte[] testPubKey =
+            Base64.Decode(
+                "mFIEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" +
+                "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8FetDVFQ0RTQSAoS2V5" +
+                "IGlzIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhQGV4YW1wbGUuY29tPoh6BBMT" +
+                "CAAiBQJRvgeoAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDqO46kgPLi" +
+                "vN1hAP4n0UApR36ziS5D8KUt7wEpBujQE4G3+efATJ+DMmY/SgEA+wbdDynFf/V8" +
+                "pQs0+FtCYQ9schzIur+peRvol7OrNnc=");
+
+        private static readonly byte[] testPrivKey =
+            Base64.Decode(
+                "lKUEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" +
+                "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8Fe/gcDAqTWSUiFpEno" +
+                "1n8izmLaWTy8GYw5/lK4R2t6D347YGgTtIiXfoNPOcosmU+3OibyTm2hc/WyG4fL" +
+                "a0nxFtj02j0Bt/Fw0N4VCKJwKL/QJT+0NUVDRFNBIChLZXkgaXMgMjU2IGJpdHMg" +
+                "bG9uZykgPHRlc3QuZWNkc2FAZXhhbXBsZS5jb20+iHoEExMIACIFAlG+B6gCGwMG" +
+                "CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOo7jqSA8uK83WEA/ifRQClHfrOJ" +
+                "LkPwpS3vASkG6NATgbf558BMn4MyZj9KAQD7Bt0PKcV/9XylCzT4W0JhD2xyHMi6" +
+                "v6l5G+iXs6s2dw==");
+
+        private static readonly char[] testPasswd = "test".ToCharArray();
+
+        private static readonly byte[] sExprKey =
+            Base64.Decode(
+                "KDIxOnByb3RlY3RlZC1wcml2YXRlLWtleSgzOmVjYyg1OmN1cnZlMTU6YnJh"
+                    + "aW5wb29sUDM4NHIxKSgxOnE5NzoEi29XCqkugtlRvONnpAVMQgfecL+Gk86O"
+                    + "t8LnUizfHG2TqRrtqlMg1DdU8Z8dJWmhJG84IUOURCyjt8nE4BeeCfRIbTU5"
+                    + "7CB13OqveBdNIRfK45UQnxHLO2MPVXf4GMdtKSg5OnByb3RlY3RlZDI1Om9w"
+                    + "ZW5wZ3AtczJrMy1zaGExLWFlcy1jYmMoKDQ6c2hhMTg6itLEzGV4Cfg4OjEy"
+                    + "OTA1NDcyKTE2OgxmufENKFTZUB72+X7AwkgpMTEyOvMWNLZgaGdlTN8XCxa6"
+                    + "8ia0Xqqb9RvHgTh+iBf0RgY5Tx5hqO9fHOi76LTBMfxs9VC4f1rTketjEUKR"
+                    + "f5amKb8lrJ67kKEsny4oRtP9ejkNzcvHFqRdxmHyL10ui8M8rJN9OU8ArqWf"
+                    + "g22dTcKu02cpKDEyOnByb3RlY3RlZC1hdDE1OjIwMTQwNjA4VDE2MDg1MCkp"
+                    + "KQ==");
+
+        private void GenerateAndSign()
+        {
+            SecureRandom random = SecureRandom.GetInstance("SHA1PRNG");
+
+            IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
+            keyGen.Init(new ECKeyGenerationParameters(SecObjectIdentifiers.SecP256r1, random));
+
+            AsymmetricCipherKeyPair kpSign = keyGen.GenerateKeyPair();
+
+            PgpKeyPair ecdsaKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.ECDsa, kpSign, DateTime.UtcNow);
+
+            byte[] msg = Encoding.ASCII.GetBytes("hello world!");
+
+            //
+            // try a signature
+            //
+            PgpSignatureGenerator signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.ECDsa, HashAlgorithmTag.Sha256);
+            signGen.InitSign(PgpSignature.BinaryDocument, ecdsaKeyPair.PrivateKey);
+
+            signGen.Update(msg);
+
+            PgpSignature sig = signGen.Generate();
+
+            sig.InitVerify(ecdsaKeyPair.PublicKey);
+            sig.Update(msg);
+
+            if (!sig.Verify())
+            {
+                Fail("signature failed to verify!");
+            }
+
+            //
+            // generate a key ring
+            //
+            char[] passPhrase = "test".ToCharArray();
+            PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator(PgpSignature.PositiveCertification, ecdsaKeyPair,
+                "test@bouncycastle.org", SymmetricKeyAlgorithmTag.Aes256, passPhrase, true, null, null, random);
+
+            PgpPublicKeyRing pubRing = keyRingGen.GeneratePublicKeyRing();
+            PgpSecretKeyRing secRing = keyRingGen.GenerateSecretKeyRing();
+
+            PgpPublicKeyRing pubRingEnc = new PgpPublicKeyRing(pubRing.GetEncoded());
+            if (!Arrays.AreEqual(pubRing.GetEncoded(), pubRingEnc.GetEncoded()))
+            {
+                Fail("public key ring encoding failed");
+            }
+
+            PgpSecretKeyRing secRingEnc = new PgpSecretKeyRing(secRing.GetEncoded());
+            if (!Arrays.AreEqual(secRing.GetEncoded(), secRingEnc.GetEncoded()))
+            {
+                Fail("secret key ring encoding failed");
+            }
+
+
+            //
+            // try a signature using encoded key
+            //
+            signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.ECDsa, HashAlgorithmTag.Sha256);
+            signGen.InitSign(PgpSignature.BinaryDocument, secRing.GetSecretKey().ExtractPrivateKey(passPhrase));
+            signGen.Update(msg);
+
+            sig = signGen.Generate();
+            sig.InitVerify(secRing.GetSecretKey().PublicKey);
+            sig.Update(msg);
+
+            if (!sig.Verify())
+            {
+                Fail("re-encoded signature failed to verify!");
+            }
+        }
+
+        public override void PerformTest()
+        {
+            //
+            // Read the public key
+            //
+            PgpPublicKeyRing pubKeyRing = new PgpPublicKeyRing(testPubKey);
+            foreach (PgpSignature certification in pubKeyRing.GetPublicKey().GetSignatures())
+            {
+                certification.InitVerify(pubKeyRing.GetPublicKey());
+
+                if (!certification.VerifyCertification((string)First(pubKeyRing.GetPublicKey().GetUserIds()), pubKeyRing.GetPublicKey()))
+                {
+                    Fail("self certification does not verify");
+                }
+            }
+
+            if (pubKeyRing.GetPublicKey().BitStrength != 256)
+            {
+                Fail("incorrect bit strength returned");
+            }
+
+            //
+            // Read the private key
+            //
+            PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing(testPrivKey);
+
+            PgpPrivateKey privKey = secretKeyRing.GetSecretKey().ExtractPrivateKey(testPasswd);
+
+            GenerateAndSign();
+
+            //
+            // sExpr
+            //
+            byte[] msg = Encoding.ASCII.GetBytes("hello world!");
+
+            PgpSecretKey key = PgpSecretKey.ParseSecretKeyFromSExpr(new MemoryStream(sExprKey, false), "test".ToCharArray());
+
+            PgpSignatureGenerator signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.ECDsa, HashAlgorithmTag.Sha256);
+            signGen.InitSign(PgpSignature.BinaryDocument, key.ExtractPrivateKey(null));
+            signGen.Update(msg);
+
+            PgpSignature sig = signGen.Generate();
+            sig.InitVerify(key.PublicKey);
+            sig.Update(msg);
+
+            if (!sig.Verify())
+            {
+                Fail("signature failed to verify!");
+            }
+        }
+
+        private static object First(IEnumerable e)
+        {
+            IEnumerator n = e.GetEnumerator();
+            Assert.IsTrue(n.MoveNext());
+            return n.Current;
+        }
+
+        public override string Name
+        {
+            get { return "PgpECDsaTest"; }
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new PgpECDsaTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/openpgp/test/PgpECMessageTest.cs b/crypto/test/src/openpgp/test/PgpECMessageTest.cs
new file mode 100644
index 000000000..ac8283721
--- /dev/null
+++ b/crypto/test/src/openpgp/test/PgpECMessageTest.cs
@@ -0,0 +1,196 @@
+using System;
+using System.IO;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
+{
+    [TestFixture]
+    public class PgpECMessageTest
+        : SimpleTest
+    {
+        private static readonly byte[] testPubKey =
+            Base64.Decode(
+                "mFIEU5SAxhMIKoZIzj0DAQcCAwRqnFLCB8EEZkAELNqznk8yQau/f1PACUTU/Qe9\n" +
+                    "jlybc22bO55BdvZdFoa3RmNQHhR980/KeVwCQ3cPpe6OQJFAtD9OSVNUIFAtMjU2\n" +
+                    "IChHZW5lcmF0ZWQgYnkgR1BHIDIuMSBiZXRhKSA8bmlzdC1wLTI1NkBleGFtcGxl\n" +
+                    "LmNvbT6IeQQTEwgAIQUCU5SAxgIbAwYLCQgHAwIGFQgCCQoLAxYCAQIeAQIXgAAK\n" +
+                    "CRA2iYNe+deDntxvAP90U2BUL2YcxrJYnsK783VIPM5U5/2IhH7azbRfaHiLZgEA\n" +
+                    "1/BVNxRG/Q07gPSdEGagRZcrzPxMQPLjBL4T7Nq5eSG4VgRTlIDqEggqhkjOPQMB\n" +
+                    "BwIDBJlWEj5qR12xbmp5dkjEkV+PRSfk37NKnw8axSJkyDTsFNZLIugMLX/zTn3r\n" +
+                    "rOamvHUdXNbLy1s8PeyrztMcOnwDAQgHiGEEGBMIAAkFAlOUgOoCGwwACgkQNomD\n" +
+                    "XvnXg556SQD+MCXRkYgLPd0NWWbCKl5wYk4NwWRvOCDFGk7eYoRTKaYBAIkt3J86\n" +
+                    "Bn0zCzsphjrIUlGPXhLSX/2aJQDuuK3zzLmn");
+
+        private static readonly byte[] sExprKeySub =
+            Base64.Decode(
+                "KDIxOnByb3RlY3RlZC1wcml2YXRlLWtleSgzOmVjYyg1OmN1cnZlMTA6TklT"
+                 + "VCBQLTI1NikoMTpxNjU6BJlWEj5qR12xbmp5dkjEkV+PRSfk37NKnw8axSJk"
+                 + "yDTsFNZLIugMLX/zTn3rrOamvHUdXNbLy1s8PeyrztMcOnwpKDk6cHJvdGVj"
+                 + "dGVkMjU6b3BlbnBncC1zMmszLXNoYTEtYWVzLWNiYygoNDpzaGExODpu2e7w"
+                 + "pW4L5jg6MTI5MDU0NzIpMTY6ohIkbi1P1O7QX1zgPd7Ejik5NjrCoM9qBxzy"
+                 + "LVJJMVRGlsjltF9/CeLnRPN1sjeiQrP1vAlZMPiOpYTmGDVRcZhdkCRO06MY"
+                 + "UTLDZK1wsxELVD0s9irpbskcOnXwqtXbIqhoK4B+9pnkR0h5gi0xPIGSTtYp"
+                 + "KDEyOnByb3RlY3RlZC1hdDE1OjIwMTQwNjA4VDE1MjgxMCkpKQ==");
+
+        private static readonly byte[] sExprKeyMaster =
+            Base64.Decode(
+                "KDIxOnByb3RlY3RlZC1wcml2YXRlLWtleSgzOmVjYyg1OmN1cnZlMTA6TklT"
+              + "VCBQLTI1NikoMTpxNjU6BGqcUsIHwQRmQAQs2rOeTzJBq79/U8AJRNT9B72O"
+              + "XJtzbZs7nkF29l0WhrdGY1AeFH3zT8p5XAJDdw+l7o5AkUApKDk6cHJvdGVj"
+              + "dGVkMjU6b3BlbnBncC1zMmszLXNoYTEtYWVzLWNiYygoNDpzaGExODr4PqHT"
+              + "9W4lpTg6MTI5MDU0NzIpMTY6VsooQy9aGsuMpiObZk4y1ik5NjoCArOSmSsJ"
+              + "IYUzxkRwy/HyDYPqjAqrNrh3m8lQco6k64Pf4SDda/0gKjkum7zYDEzBEvXI"
+              + "+ZodAST6z3IDkPHL7LUy5qp2LdG73xLRFjfsqOsZgP+nwoOSUiC7N4AWJPAp"
+              + "KDEyOnByb3RlY3RlZC1hdDE1OjIwMTQwNjA4VDE1MjcwOSkpKQ==");
+
+        private static readonly byte[] encMessage =
+            Base64.Decode("hH4DrQCblwYU61MSAgMEVXjgPW2hvIhUMQ2qlAQlAliZKbyujaYfLnwZTeGvu+pt\n"+
+                "gJXt+JJ8zWoENxLAp+Nb3PxJW4CjvkXQ2dEmmvkhBzAhDer86XJBrQLBQUL+6EmE\n"+
+                "l+/3Yzt+cPEyEn32BSpkt31F2yGncoefCUDgj9tKiFXSRwGhjRno0qzB3CfRWzDu\n"+
+                "eelwwtRcxnvXNc44TuHRf4PgZ3d4dDU69bWQswdQ5UTP/Bjjo92yMLtJ3HtBuym+\n"+
+                "NazbQUh4M+SP");
+
+        private static readonly byte[] signedEncMessage =
+            Base64.Decode("hH4DrQCblwYU61MSAgMEC/jpqjgnqotzKWNWJ3bhOxmmChghrV2PLQbQqtHtVvbj\n" +
+                "zyLpaPgeqLslMAjsdy8rlANCjlweZhtP1DmvHiYgjDAA54eptpLMtbULaQOoRcsZ\n" +
+                "ZnMqhx9s5phAohNFGC+DnVU/IwxDOnI+ya54LOoXUrrSsgEKDTlAmYr4/oDmLTXt\n" +
+                "TaLgk0T9nBxGe8WbLwhPRBIyq6NX151aQ+pOobajrRiLwg/CwUsbAZ50bBPn2JjX\n" +
+                "wgBhBjyAn7D6bZ4hMl3YSluSiFkJhxZcYSydtIAlX35q4D/pJjT4mPT/y7ypytCU\n" +
+                "0wWo53O6NCSeM/EpeFw8RRh8fe+m33qpA6T5sR3Alg4ZukiIxLa36k6Cv5KTHmB3\n" +
+                "6lKZcgQDHNIKStV1bW4Cva1aXXQ=");
+
+        private void DoTestMasterKey()
+        {
+            PgpSecretKey key = PgpSecretKey.ParseSecretKeyFromSExpr(new MemoryStream(sExprKeyMaster, false),
+                "test".ToCharArray());
+
+            byte[] msg = Encoding.UTF8.GetBytes("hello world!");
+
+            PgpSignatureGenerator signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.ECDsa, HashAlgorithmTag.Sha256);
+            signGen.InitSign(PgpSignature.BinaryDocument, key.ExtractPrivateKey(null));
+            signGen.Update(msg);
+            PgpSignature sig = signGen.Generate();
+
+            PgpPublicKey publicKey = new PgpPublicKeyRing(testPubKey).GetPublicKey();
+            sig.InitVerify(publicKey);
+            sig.Update(msg);
+
+            if (!sig.Verify())
+            {
+                Fail("signature failed to verify!");
+            }
+        }
+
+        private void DoTestEncMessage()
+        {
+            PgpObjectFactory pgpFact = new PgpObjectFactory(encMessage);
+
+            PgpEncryptedDataList encList = (PgpEncryptedDataList)pgpFact.NextPgpObject();
+
+            PgpPublicKeyEncryptedData encP = (PgpPublicKeyEncryptedData)encList[0];
+
+            PgpPublicKey publicKey = new PgpPublicKeyRing(testPubKey).GetPublicKey(encP.KeyId);
+
+            PgpSecretKey secretKey = PgpSecretKey.ParseSecretKeyFromSExpr(new MemoryStream(sExprKeySub, false),
+                "test".ToCharArray(), publicKey);
+
+            Stream clear = encP.GetDataStream(secretKey.ExtractPrivateKey(null));
+
+            PgpObjectFactory plainFact = new PgpObjectFactory(clear);
+
+            PgpCompressedData cData = (PgpCompressedData)plainFact.NextPgpObject();
+
+            PgpObjectFactory compFact = new PgpObjectFactory(cData.GetDataStream());
+
+            PgpLiteralData lData = (PgpLiteralData)compFact.NextPgpObject();
+
+            if (!"test.txt".Equals(lData.FileName))
+            {
+                Fail("wrong file name detected");
+            }
+        }
+
+        private void DoTestSignedEncMessage()
+        {
+            PgpObjectFactory pgpFact = new PgpObjectFactory(signedEncMessage);
+
+            PgpEncryptedDataList encList = (PgpEncryptedDataList)pgpFact.NextPgpObject();
+
+            PgpPublicKeyEncryptedData encP = (PgpPublicKeyEncryptedData)encList[0];
+
+            PgpPublicKeyRing publicKeyRing = new PgpPublicKeyRing(testPubKey);
+
+            PgpPublicKey publicKey = publicKeyRing.GetPublicKey(encP.KeyId);
+
+            PgpSecretKey secretKey = PgpSecretKey.ParseSecretKeyFromSExpr(new MemoryStream(sExprKeySub, false),
+                "test".ToCharArray(), publicKey);
+
+            Stream clear = encP.GetDataStream(secretKey.ExtractPrivateKey(null));
+
+            PgpObjectFactory plainFact = new PgpObjectFactory(clear);
+
+            PgpCompressedData cData = (PgpCompressedData)plainFact.NextPgpObject();
+
+            PgpObjectFactory compFact = new PgpObjectFactory(cData.GetDataStream());
+
+            PgpOnePassSignatureList sList = (PgpOnePassSignatureList)compFact.NextPgpObject();
+
+            PgpOnePassSignature ops = sList[0];
+
+            PgpLiteralData lData  = (PgpLiteralData)compFact.NextPgpObject();
+
+            if (!"test.txt".Equals(lData.FileName))
+            {
+                Fail("wrong file name detected");
+            }
+
+            Stream dIn = lData .GetInputStream();
+
+            ops.InitVerify(publicKeyRing.GetPublicKey(ops.KeyId));
+
+            int ch;
+            while ((ch = dIn.ReadByte()) >= 0)
+            {
+                ops.Update((byte)ch);
+            }
+
+            PgpSignatureList p3 = (PgpSignatureList)compFact.NextPgpObject();
+
+            if (!ops.Verify(p3[0]))
+            {
+                Fail("Failed signature check");
+            }
+        }
+
+        public override void PerformTest()
+        {
+            DoTestMasterKey();
+            DoTestEncMessage();
+            DoTestSignedEncMessage();
+        }
+
+        public override string Name
+        {
+            get { return "PgpECMessageTest"; }
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new PgpECMessageTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/openpgp/test/PgpKeyRingTest.cs b/crypto/test/src/openpgp/test/PgpKeyRingTest.cs
index 2ac2f0c97..9896c1ef6 100644
--- a/crypto/test/src/openpgp/test/PgpKeyRingTest.cs
+++ b/crypto/test/src/openpgp/test/PgpKeyRingTest.cs
@@ -2145,6 +2145,40 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
             PublicKeyRingWithX509Test();
             SecretKeyRingWithPersonalCertificateTest();
             InsertMasterTest();
+            LongSubPacketsTest();
+        }
+
+        private void LongSubPacketsTest()
+        {
+            Stream fIn = SimpleTest.GetTestDataAsStream("openpgp.longSigSubPack.asc");
+            Stream bIn = new BufferedStream(fIn);
+            PgpPublicKeyRing pkr = new PgpPublicKeyRing(PgpUtilities.GetDecoderStream(bIn));
+            bIn.Close();
+
+            PgpPublicKey masterpk = pkr.GetPublicKey();
+
+            // Check userids
+            foreach (string uid in masterpk.GetUserIds())
+            {
+                CheckUidSig(masterpk, uid);
+            }
+        }
+
+        private void CheckUidSig(PgpPublicKey pk, string uid)
+        {
+            foreach (PgpSignature sig in pk.GetSignaturesForId(uid))
+            {
+                if (!IsGoodUidSignature(sig, pk, uid))
+                {
+                    Fail("Bad self-signature found for '" + uid + "'");
+                }
+            }
+        }
+
+        private static bool IsGoodUidSignature(PgpSignature sig, PgpPublicKey masterpk, string uid)
+        {
+            sig.InitVerify(masterpk);
+            return sig.VerifyCertification(uid, masterpk);
         }
 
         public override string Name
diff --git a/crypto/test/src/openpgp/test/PgpParsingTest.cs b/crypto/test/src/openpgp/test/PgpParsingTest.cs
new file mode 100644
index 000000000..78fca7570
--- /dev/null
+++ b/crypto/test/src/openpgp/test/PgpParsingTest.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
+{
+	[TestFixture]
+	public class PgpParsingTest
+		: SimpleTest
+	{
+		public override void PerformTest()
+		{
+            Stream fIn = SimpleTest.GetTestDataAsStream("openpgp.bigpub.asc");
+            Stream keyIn = PgpUtilities.GetDecoderStream(fIn);
+            PgpPublicKeyRingBundle pubRings = new PgpPublicKeyRingBundle(keyIn);
+		}
+
+        public override string Name
+		{
+			get { return "PgpParsingTest"; }
+		}
+
+        public static void Main(
+			string[] args)
+		{
+			RunTest(new PgpParsingTest());
+		}
+
+        [Test]
+		public void TestFunction()
+		{
+			string resultText = Perform().ToString();
+
+			Assert.AreEqual(Name + ": Okay", resultText);
+		}
+	}
+}
diff --git a/crypto/test/src/openpgp/test/PgpUnicodeTest.cs b/crypto/test/src/openpgp/test/PgpUnicodeTest.cs
new file mode 100644
index 000000000..ce1df8980
--- /dev/null
+++ b/crypto/test/src/openpgp/test/PgpUnicodeTest.cs
@@ -0,0 +1,137 @@
+using System;
+using System.IO;
+using System.Text;
+
+using NUnit.Core;
+using NUnit.Framework;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
+{
+    [TestFixture]
+    public class PgpUnicodeTest
+    {
+        private void DoTestKey(BigInteger keyId, string passphrase)
+        {
+            PgpSecretKeyRingBundle secretKeyRing = LoadSecretKeyCollection("secring.gpg");
+
+            PgpSecretKeyRing secretKey = secretKeyRing.GetSecretKeyRing(keyId.LongValue);
+            Assert.NotNull(secretKey, "Could not locate secret keyring with Id=" + keyId.ToString(16));
+
+            PgpSecretKey key = secretKey.GetSecretKey();
+            Assert.NotNull(key, "Could not locate secret key!");
+
+            try
+            {
+                PgpPrivateKey privateKey = key.ExtractPrivateKey(passphrase.ToCharArray());
+
+                Assert.IsTrue(privateKey.KeyId == keyId.LongValue);
+            }
+            catch (PgpException e)
+            {
+                throw new PgpException("Password incorrect!", e);
+            }
+
+            // all fine!
+        }
+
+        [Test]
+        public void TestUmlautPassphrase()
+        {
+
+            try
+            {
+                BigInteger keyId = new BigInteger("362961283C48132B9F14C5C3EC87272EFCB986D2", 16);
+
+                string passphrase = Encoding.Unicode.GetString(Encoding.Unicode.GetBytes("Händle"));
+
+ //             FileInputStream passwordFile = new FileInputStream("testdata/passphrase_for_test.txt");
+ //             byte[] password = new byte[passwordFile.available()];
+ //             passwordFile.read(password);
+ //             passwordFile.close();
+ //             String passphrase = new String(password);            
+
+                DoTestKey(keyId, passphrase);
+
+                // all fine!
+
+            }
+            catch (Exception e)
+            {
+                Console.Error.WriteLine(e.StackTrace);
+                Assert.Fail(e.Message);
+            }
+        }
+
+        [Test]
+        public void TestAsciiPassphrase()
+        {
+
+            try
+            {
+                BigInteger keyId = new BigInteger("A392B7310C64026022405257AA2AAAC7CB417459", 16);
+
+                string passphrase = "Admin123";
+
+                DoTestKey(keyId, passphrase);
+
+                // all fine!
+            }
+            catch (Exception e)
+            {
+                Console.Error.WriteLine(e.StackTrace);
+                Assert.Fail(e.Message);
+            }
+        }
+
+        [Test]
+        public void TestCyrillicPassphrase()
+        {
+
+            try
+            {
+                BigInteger keyId = new BigInteger("B7773AF32BE4EC1806B1BACC4680E7F3960C44E7", 16);
+
+                // XXX The password text file must not have the UTF-8 BOM !
+                // Ref: http://stackoverflow.com/questions/2223882/whats-different-between-utf-8-and-utf-8-without-bom
+
+                Stream passwordFile = SimpleTest.GetTestDataAsStream("openpgp.unicode.passphrase_cyr.txt");
+                TextReader reader = new StreamReader(passwordFile, Encoding.UTF8);
+                string passphrase = reader.ReadLine();
+                passwordFile.Close();
+
+                DoTestKey(keyId, passphrase);
+
+                // all fine!
+            }
+            catch (Exception e)
+            {
+                Console.Error.WriteLine(e.StackTrace);
+                Assert.Fail(e.Message);
+            }
+        }
+
+        private PgpSecretKeyRingBundle LoadSecretKeyCollection(string keyName)
+        {
+            return new PgpSecretKeyRingBundle(SimpleTest.GetTestDataAsStream("openpgp.unicode." + keyName));
+        }
+
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
+
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("Unicode Password Tests");
+                suite.Add(new PgpUnicodeTest());
+                return suite;
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/openpgp/test/RegressionTest.cs b/crypto/test/src/openpgp/test/RegressionTest.cs
index e8a3685c6..c784a19c8 100644
--- a/crypto/test/src/openpgp/test/RegressionTest.cs
+++ b/crypto/test/src/openpgp/test/RegressionTest.cs
@@ -20,6 +20,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
             new PgpClearSignedSignatureTest(),
             new PgpCompressionTest(),
             new PGPNoPrivateKeyTest(),
+            new PgpECDHTest(),
+            new PgpECDsaTest(),
+            new PgpECMessageTest(),
+            new PgpParsingTest(),
         };
 
         public static void Main(
diff --git a/crypto/test/src/openssl/test/AllTests.cs b/crypto/test/src/openssl/test/AllTests.cs
index 921208179..f843d0479 100644
--- a/crypto/test/src/openssl/test/AllTests.cs
+++ b/crypto/test/src/openssl/test/AllTests.cs
@@ -32,19 +32,24 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 				return (char[]) password.Clone();
 			}
 		}
-		
-		[Suite]
-		public static TestSuite Suite
-		{
-			get
-			{
-				TestSuite suite = new TestSuite("OpenSSL Tests");
-				suite.Add(new AllTests());
-				return suite;
-			}
-		}
 
-		[Test]
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
+
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("OpenSSL Tests");
+                suite.Add(new AllTests());
+                return suite;
+            }
+        }
+
+        [Test]
 		public void TestOpenSsl()
 		{
 			Org.BouncyCastle.Utilities.Test.ITest[] tests = new Org.BouncyCastle.Utilities.Test.ITest[]{
@@ -121,13 +126,5 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 
 			Assert.AreEqual(privKey, rdKey);
 		}
-
-        public static void Main(
-			string[] args)
-        {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            Suite.Run(el);
-        }
 	}
 }
diff --git a/crypto/test/src/security/test/SecureRandomTest.cs b/crypto/test/src/security/test/SecureRandomTest.cs
index 12e4b9a47..4f05a286a 100644
--- a/crypto/test/src/security/test/SecureRandomTest.cs
+++ b/crypto/test/src/security/test/SecureRandomTest.cs
@@ -1,150 +1,165 @@
 using System;
+using System.Text;
 
 using NUnit.Framework;
 
 using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Security.Tests
 {
-	[TestFixture]
-	public class SecureRandomTest
-	{
+    [TestFixture]
+    public class SecureRandomTest
+    {
 #if !NETCF_1_0
-		[Test]
-		public void TestCryptoApi()
-		{
-			SecureRandom random = new SecureRandom(
-				new CryptoApiRandomGenerator());
-
-			checkSecureRandom(random);
-		}
+        [Test]
+        public void TestCryptoApi()
+        {
+            SecureRandom random = new SecureRandom(
+                new CryptoApiRandomGenerator());
+
+            CheckSecureRandom(random);
+        }
 #endif
 
-		[Test]
-		public void TestDefault()
-		{
-			SecureRandom random = new SecureRandom();
-
-			checkSecureRandom(random);
-		}
-
-		[Test]
-		public void TestSha1Prng()
-		{
-			SecureRandom random = SecureRandom.GetInstance("SHA1PRNG");
-			random.SetSeed(SecureRandom.GetSeed(20));
-
-			checkSecureRandom(random);
-		}
-
-		[Test]
-		public void TestSha256Prng()
-		{
-			SecureRandom random = SecureRandom.GetInstance("SHA256PRNG");
-			random.SetSeed(SecureRandom.GetSeed(32));
-
-			checkSecureRandom(random);
-		}
-
-		[Test]
-		public void TestThreadedSeed()
-		{
-			SecureRandom random = new SecureRandom(
-				new ThreadedSeedGenerator().GenerateSeed(20, false));
-
-			checkSecureRandom(random);
-		}
-
-		[Test]
-		public void TestVmpcPrng()
-		{
-			SecureRandom random = new SecureRandom(new VmpcRandomGenerator());
-			random.SetSeed(SecureRandom.GetSeed(32));
-
-			checkSecureRandom(random);
-		}
-
-
-		private static void checkSecureRandom(
-			SecureRandom random)
-		{
-			// Note: This will periodically (< 1e-6 probability) give a false alarm.
-			// That's randomness for you!
-			Assert.IsTrue(runChiSquaredTests(random), "Chi2 test detected possible non-randomness");
-		}
-
-		private static bool runChiSquaredTests(
-			SecureRandom random)
-		{
-			int passes = 0;
-
-			for (int tries = 0; tries < 100; ++tries)
-			{
-				double chi2 = measureChiSquared(random, 1000);
-				if (chi2 < 285.0) // 255 degrees of freedom in test => Q ~ 10.0% for 285
-					++passes;
-			}
-
-			return passes > 75;
-		}
-
-		private static double measureChiSquared(
-			SecureRandom	random,
-			int				rounds)
-		{
-			int[] counts = new int[256];
-
-			byte[] bs = new byte[256];
-			for (int i = 0; i < rounds; ++i)
-			{
-				random.NextBytes(bs);
-
-				for (int b = 0; b < 256; ++b)
-				{
-					++counts[bs[b]];
-				}
-			}
-
-			byte mask = SecureRandom.GetSeed(1)[0];
-			for (int i = 0; i < rounds; ++i)
-			{
-				random.NextBytes(bs);
-
-				for (int b = 0; b < 256; ++b)
-				{
-					++counts[bs[b] ^ mask];
-				}
-
-				++mask;
-			}
-
-			byte shift = SecureRandom.GetSeed(1)[0];
-			for (int i = 0; i < rounds; ++i)
-			{
-				random.NextBytes(bs);
-
-				for (int b = 0; b < 256; ++b)
-				{
-					++counts[(byte)(bs[b] + shift)];
-				}
-
-				++shift;
-			}
-
-			int total = 3 * rounds;
-
-			double chi2 = 0;
-			for (int k = 0; k < counts.Length; ++k)
-			{
-				double diff = ((double) counts[k]) - total;
-				double diff2 = diff * diff;
-
-				chi2 += diff2;
-			}
-
-			chi2 /= total;
-
-			return chi2;
-		}
-	}
+        [Test]
+        public void TestDefault()
+        {
+            SecureRandom random = new SecureRandom();
+
+            CheckSecureRandom(random);
+        }
+
+        [Test]
+        public void TestSha1Prng()
+        {
+            SecureRandom random = SecureRandom.GetInstance("SHA1PRNG");
+
+            CheckSecureRandom(random);
+        }
+
+        [Test]
+        public void TestSha1PrngBackward()
+        {
+            byte[] seed = Encoding.ASCII.GetBytes("backward compatible");
+
+            SecureRandom sx = new SecureRandom(seed);
+            SecureRandom sy = SecureRandom.GetInstance("SHA1PRNG", false); sy.SetSeed(seed);
+
+            byte[] bx = new byte[128]; sx.NextBytes(bx);
+            byte[] by = new byte[128]; sy.NextBytes(by);
+
+            Assert.IsTrue(Arrays.AreEqual(bx, by));
+        }
+
+        [Test]
+        public void TestSha256Prng()
+        {
+            SecureRandom random = SecureRandom.GetInstance("SHA256PRNG");
+
+            CheckSecureRandom(random);
+        }
+
+        [Test]
+        public void TestThreadedSeed()
+        {
+            SecureRandom random = SecureRandom.GetInstance("SHA1PRNG", false);
+            random.SetSeed(new ThreadedSeedGenerator().GenerateSeed(20, false));
+
+            CheckSecureRandom(random);
+        }
+
+        [Test]
+        public void TestVmpcPrng()
+        {
+            SecureRandom random = new SecureRandom(new VmpcRandomGenerator());
+            random.SetSeed(SecureRandom.GetSeed(32));
+
+            CheckSecureRandom(random);
+        }
+
+
+        private static void CheckSecureRandom(SecureRandom random)
+        {
+            // Note: This will periodically (< 1e-6 probability) give a false alarm.
+            // That's randomness for you!
+            Assert.IsTrue(RunChiSquaredTests(random), "Chi2 test detected possible non-randomness");
+        }
+
+        private static bool RunChiSquaredTests(SecureRandom random)
+        {
+            int passes = 0;
+
+            for (int tries = 0; tries < 100; ++tries)
+            {
+                double chi2 = MeasureChiSquared(random, 1000);
+
+                // 255 degrees of freedom in test => Q ~ 10.0% for 285
+                if (chi2 < 285.0)
+                {
+                    ++passes;
+                }
+            }
+
+            return passes > 75;
+        }
+
+        private static double MeasureChiSquared(SecureRandom random, int rounds)
+        {
+            byte[] opts = SecureRandom.GetSeed(2);
+            int[] counts = new int[256];
+
+            byte[] bs = new byte[256];
+            for (int i = 0; i < rounds; ++i)
+            {
+                random.NextBytes(bs);
+
+                for (int b = 0; b < 256; ++b)
+                {
+                    ++counts[bs[b]];
+                }
+            }
+
+            byte mask = opts[0];
+            for (int i = 0; i < rounds; ++i)
+            {
+                random.NextBytes(bs);
+
+                for (int b = 0; b < 256; ++b)
+                {
+                    ++counts[bs[b] ^ mask];
+                }
+
+                ++mask;
+            }
+
+            byte shift = opts[1];
+            for (int i = 0; i < rounds; ++i)
+            {
+                random.NextBytes(bs);
+
+                for (int b = 0; b < 256; ++b)
+                {
+                    ++counts[(byte)(bs[b] + shift)];
+                }
+
+                ++shift;
+            }
+
+            int total = 3 * rounds;
+
+            double chi2 = 0;
+            for (int k = 0; k < counts.Length; ++k)
+            {
+                double diff = ((double) counts[k]) - total;
+                double diff2 = diff * diff;
+
+                chi2 += diff2;
+            }
+
+            chi2 /= total;
+
+            return chi2;
+        }
+    }
 }
diff --git a/crypto/test/src/test/CertPathValidatorTest.cs b/crypto/test/src/test/CertPathValidatorTest.cs
index a92bb9b31..f83ac850a 100644
--- a/crypto/test/src/test/CertPathValidatorTest.cs
+++ b/crypto/test/src/test/CertPathValidatorTest.cs
@@ -290,7 +290,7 @@ namespace Org.BouncyCastle.Tests
                 return null;
             }
 
-            public override void Check(X509Certificate cert, ICollection unresolvedCritExts)
+            public override void Check(X509Certificate cert, ISet unresolvedCritExts)
             {
                 count++;
             }
diff --git a/crypto/test/src/tsp/test/AllTests.cs b/crypto/test/src/tsp/test/AllTests.cs
index 66dc9c480..3e8b0cd5e 100644
--- a/crypto/test/src/tsp/test/AllTests.cs
+++ b/crypto/test/src/tsp/test/AllTests.cs
@@ -9,24 +9,23 @@ namespace Org.BouncyCastle.Tsp.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-		public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("TSP Tests");
-
-			suite.Add(new GenTimeAccuracyUnitTest());
-			suite.Add(new ParseTest());
-			suite.Add(new TimeStampTokenInfoUnitTest());
-			suite.Add(new TspTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("TSP Tests");
+                suite.Add(new GenTimeAccuracyUnitTest());
+                suite.Add(new ParseTest());
+                suite.Add(new TimeStampTokenInfoUnitTest());
+                suite.Add(new TspTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/util/io/pem/test/AllTests.cs b/crypto/test/src/util/io/pem/test/AllTests.cs
index b44949383..c36f79304 100644
--- a/crypto/test/src/util/io/pem/test/AllTests.cs
+++ b/crypto/test/src/util/io/pem/test/AllTests.cs
@@ -19,18 +19,23 @@ namespace Org.BouncyCastle.Utilities.IO.Pem.Tests
 	[TestFixture]
 	public class AllTests
 	{
-		[Suite]
-		public static TestSuite Suite
-		{
-			get
-			{
-				TestSuite suite = new TestSuite("PEM Utilities Tests");
-				suite.Add(new AllTests());
-				return suite;
-			}
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		[Test]
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("PEM Utilities Tests");
+                suite.Add(new AllTests());
+                return suite;
+            }
+        }
+
+        [Test]
 		public void TestPemLength()
 		{
 			for (int i = 1; i != 60; i++)
@@ -65,13 +70,5 @@ namespace Org.BouncyCastle.Utilities.IO.Pem.Tests
 
 			Assert.AreEqual(sw.ToString().Length, pWrt.GetOutputSize(pemObj));
 		}
-
-		public static void Main(
-			string[] args)
-        {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            Suite.Run(el);
-        }
 	}
 }
diff --git a/crypto/test/src/util/test/UncloseableStream.cs b/crypto/test/src/util/test/UncloseableStream.cs
index 8b592cdd4..171198120 100644
--- a/crypto/test/src/util/test/UncloseableStream.cs
+++ b/crypto/test/src/util/test/UncloseableStream.cs
@@ -1,7 +1,7 @@
 using System;
 using System.IO;
 
-using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Utilities.Test
 {
diff --git a/crypto/test/src/x509/test/TestCertificateGen.cs b/crypto/test/src/x509/test/TestCertificateGen.cs
index 8ec5929c7..e91a102f1 100644
--- a/crypto/test/src/x509/test/TestCertificateGen.cs
+++ b/crypto/test/src/x509/test/TestCertificateGen.cs
@@ -12,12 +12,21 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Utilities.Collections;
 using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.X509.Tests
 {
     [TestFixture]
-    public class TestCertificateGen
+    public class TestCertificateGen: SimpleTest
     {
+        public override string Name
+        {
+            get
+            {
+                return "X.509 Cert Gen";
+            }
+        }
+
         public TestCertificateGen()
         {
         }
@@ -260,6 +269,7 @@ namespace Org.BouncyCastle.X509.Tests
             certGen.SetPublicKey(ecPub);
             certGen.SetSignatureAlgorithm("SHA1WITHECDSA");
 
+            certGen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
 
             X509Certificate cert = certGen.Generate(ecPriv);
 
@@ -267,8 +277,12 @@ namespace Org.BouncyCastle.X509.Tests
 			cert.CheckValidity();
 			cert.Verify(ecPub);
 
-            //ISet dummySet = cert.GetNonCriticalExtensionOids();
+            ISet extOidSet = cert.GetCriticalExtensionOids();
 
+            if (extOidSet.Count != 1)
+            {
+                Fail("wrong number of oids");
+            }
             //if (dummySet != null)
             //{
             //    foreach (string key in dummySet)
@@ -711,5 +725,17 @@ namespace Org.BouncyCastle.X509.Tests
 				Assert.Fail("Reading busted Certificate.");
 			}
 		}
-	}
+
+        public static void Main(string[] args)
+        {
+            RunTest(new TestCertificateGen());
+        }
+
+        public override void PerformTest()
+        {
+            TestCreationRSA();
+            TestCreationDSA();
+            TestCreationECDSA();
+        }
+    }
 }