diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index 592dcc977..575c753ea 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -1560,6 +1560,272 @@
<Compile Include="src\security\cert\CertificateNotYetValidException.cs" />
<Compile Include="src\security\cert\CertificateParsingException.cs" />
<Compile Include="src\security\cert\CrlException.cs" />
+ <Compile Include="src\tls\AbstractTlsClient.cs" />
+ <Compile Include="src\tls\AbstractTlsContext.cs" />
+ <Compile Include="src\tls\AbstractTlsKeyExchange.cs" />
+ <Compile Include="src\tls\AbstractTlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\AbstractTlsPeer.cs" />
+ <Compile Include="src\tls\AbstractTlsServer.cs" />
+ <Compile Include="src\tls\AlertDescription.cs" />
+ <Compile Include="src\tls\AlertLevel.cs" />
+ <Compile Include="src\tls\BasicTlsPskIdentity.cs" />
+ <Compile Include="src\tls\BasicTlsSrpIdentity.cs" />
+ <Compile Include="src\tls\ByteQueue.cs" />
+ <Compile Include="src\tls\ByteQueueInputStream.cs" />
+ <Compile Include="src\tls\ByteQueueOutputStream.cs" />
+ <Compile Include="src\tls\CachedInformationType.cs" />
+ <Compile Include="src\tls\CertChainType.cs" />
+ <Compile Include="src\tls\Certificate.cs" />
+ <Compile Include="src\tls\CertificateEntry.cs" />
+ <Compile Include="src\tls\CertificateRequest.cs" />
+ <Compile Include="src\tls\CertificateStatus.cs" />
+ <Compile Include="src\tls\CertificateStatusRequest.cs" />
+ <Compile Include="src\tls\CertificateStatusRequestItemV2.cs" />
+ <Compile Include="src\tls\CertificateStatusType.cs" />
+ <Compile Include="src\tls\CertificateType.cs" />
+ <Compile Include="src\tls\CertificateUrl.cs" />
+ <Compile Include="src\tls\ChangeCipherSpec.cs" />
+ <Compile Include="src\tls\ChannelBinding.cs" />
+ <Compile Include="src\tls\CipherSuite.cs" />
+ <Compile Include="src\tls\CipherType.cs" />
+ <Compile Include="src\tls\ClientAuthenticationType.cs" />
+ <Compile Include="src\tls\ClientCertificateType.cs" />
+ <Compile Include="src\tls\ClientHello.cs" />
+ <Compile Include="src\tls\CombinedHash.cs" />
+ <Compile Include="src\tls\CompressionMethod.cs" />
+ <Compile Include="src\tls\ConnectionEnd.cs" />
+ <Compile Include="src\tls\ContentType.cs" />
+ <Compile Include="src\tls\crypto\CryptoHashAlgorithm.cs" />
+ <Compile Include="src\tls\crypto\CryptoSignatureAlgorithm.cs" />
+ <Compile Include="src\tls\crypto\DHGroup.cs" />
+ <Compile Include="src\tls\crypto\DHStandardGroups.cs" />
+ <Compile Include="src\tls\crypto\impl\AbstractTlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\impl\AbstractTlsSecret.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcChaCha20Poly1305.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedAgreement.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedDecryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcSsl3Hmac.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsAeadCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsBlockCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsCertificate.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDH.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDHDomain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDssSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDssVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDH.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDomain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsa13Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsa13Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd25519Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd25519Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd448Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd448Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsHash.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsHmac.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsNonceGenerator.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaEncryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaPssSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaPssVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSecret.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6Client.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6Server.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6VerifierGenerator.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsStreamVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcVerifyingStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX25519.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX25519Domain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX448.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX448Domain.cs" />
+ <Compile Include="src\tls\crypto\impl\RsaUtilities.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsAeadCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsAeadCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsBlockCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsBlockCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsEncryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsImplUtilities.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsNullCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsSuiteHmac.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsSuiteMac.cs" />
+ <Compile Include="src\tls\crypto\Srp6Group.cs" />
+ <Compile Include="src\tls\crypto\Srp6StandardGroups.cs" />
+ <Compile Include="src\tls\crypto\TlsAgreement.cs" />
+ <Compile Include="src\tls\crypto\TlsCertificate.cs" />
+ <Compile Include="src\tls\crypto\TlsCertificateRole.cs" />
+ <Compile Include="src\tls\crypto\TlsCipher.cs" />
+ <Compile Include="src\tls\crypto\TlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoException.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoParameters.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoUtilities.cs" />
+ <Compile Include="src\tls\crypto\TlsDecodeResult.cs" />
+ <Compile Include="src\tls\crypto\TlsDHConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsDHDomain.cs" />
+ <Compile Include="src\tls\crypto\TlsECConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsECDomain.cs" />
+ <Compile Include="src\tls\crypto\TlsEncodeResult.cs" />
+ <Compile Include="src\tls\crypto\TlsHash.cs" />
+ <Compile Include="src\tls\crypto\TlsHashSink.cs" />
+ <Compile Include="src\tls\crypto\TlsHmac.cs" />
+ <Compile Include="src\tls\crypto\TlsMac.cs" />
+ <Compile Include="src\tls\crypto\TlsMacSink.cs" />
+ <Compile Include="src\tls\crypto\TlsNonceGenerator.cs" />
+ <Compile Include="src\tls\crypto\TlsNullNullCipher.cs" />
+ <Compile Include="src\tls\crypto\TlsSecret.cs" />
+ <Compile Include="src\tls\crypto\TlsSigner.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6Client.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6Server.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6VerifierGenerator.cs" />
+ <Compile Include="src\tls\crypto\TlsSrpConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\TlsStreamVerifier.cs" />
+ <Compile Include="src\tls\crypto\TlsVerifier.cs" />
+ <Compile Include="src\tls\DatagramReceiver.cs" />
+ <Compile Include="src\tls\DatagramSender.cs" />
+ <Compile Include="src\tls\DatagramTransport.cs" />
+ <Compile Include="src\tls\DefaultTlsClient.cs" />
+ <Compile Include="src\tls\DefaultTlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\DefaultTlsDHGroupVerifier.cs" />
+ <Compile Include="src\tls\DefaultTlsHeartbeat.cs" />
+ <Compile Include="src\tls\DefaultTlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\DefaultTlsServer.cs" />
+ <Compile Include="src\tls\DefaultTlsSrpConfigVerifier.cs" />
+ <Compile Include="src\tls\DeferredHash.cs" />
+ <Compile Include="src\tls\DigestInputBuffer.cs" />
+ <Compile Include="src\tls\DigitallySigned.cs" />
+ <Compile Include="src\tls\DtlsClientProtocol.cs" />
+ <Compile Include="src\tls\DtlsEpoch.cs" />
+ <Compile Include="src\tls\DtlsHandshakeRetransmit.cs" />
+ <Compile Include="src\tls\DtlsProtocol.cs" />
+ <Compile Include="src\tls\DtlsReassembler.cs" />
+ <Compile Include="src\tls\DtlsRecordLayer.cs" />
+ <Compile Include="src\tls\DtlsReliableHandshake.cs" />
+ <Compile Include="src\tls\DtlsReplayWindow.cs" />
+ <Compile Include="src\tls\DtlsRequest.cs" />
+ <Compile Include="src\tls\DtlsServerProtocol.cs" />
+ <Compile Include="src\tls\DtlsTransport.cs" />
+ <Compile Include="src\tls\DtlsVerifier.cs" />
+ <Compile Include="src\tls\ECCurveType.cs" />
+ <Compile Include="src\tls\ECPointFormat.cs" />
+ <Compile Include="src\tls\EncryptionAlgorithm.cs" />
+ <Compile Include="src\tls\ExporterLabel.cs" />
+ <Compile Include="src\tls\ExtensionType.cs" />
+ <Compile Include="src\tls\HandshakeMessageInput.cs" />
+ <Compile Include="src\tls\HandshakeMessageOutput.cs" />
+ <Compile Include="src\tls\HandshakeType.cs" />
+ <Compile Include="src\tls\HashAlgorithm.cs" />
+ <Compile Include="src\tls\HeartbeatExtension.cs" />
+ <Compile Include="src\tls\HeartbeatMessage.cs" />
+ <Compile Include="src\tls\HeartbeatMessageType.cs" />
+ <Compile Include="src\tls\HeartbeatMode.cs" />
+ <Compile Include="src\tls\IdentifierType.cs" />
+ <Compile Include="src\tls\KeyExchangeAlgorithm.cs" />
+ <Compile Include="src\tls\KeyShareEntry.cs" />
+ <Compile Include="src\tls\KeyUpdateRequest.cs" />
+ <Compile Include="src\tls\MacAlgorithm.cs" />
+ <Compile Include="src\tls\MaxFragmentLength.cs" />
+ <Compile Include="src\tls\NamedGroup.cs" />
+ <Compile Include="src\tls\NamedGroupRole.cs" />
+ <Compile Include="src\tls\NameType.cs" />
+ <Compile Include="src\tls\NewSessionTicket.cs" />
+ <Compile Include="src\tls\OcspStatusRequest.cs" />
+ <Compile Include="src\tls\OfferedPsks.cs" />
+ <Compile Include="src\tls\PrfAlgorithm.cs" />
+ <Compile Include="src\tls\ProtocolName.cs" />
+ <Compile Include="src\tls\ProtocolVersion.cs" />
+ <Compile Include="src\tls\PskIdentity.cs" />
+ <Compile Include="src\tls\PskKeyExchangeMode.cs" />
+ <Compile Include="src\tls\PskTlsClient.cs" />
+ <Compile Include="src\tls\PskTlsServer.cs" />
+ <Compile Include="src\tls\RecordFormat.cs" />
+ <Compile Include="src\tls\RecordPreview.cs" />
+ <Compile Include="src\tls\RecordStream.cs" />
+ <Compile Include="src\tls\SecurityParameters.cs" />
+ <Compile Include="src\tls\ServerHello.cs" />
+ <Compile Include="src\tls\ServerName.cs" />
+ <Compile Include="src\tls\ServerNameList.cs" />
+ <Compile Include="src\tls\ServerOnlyTlsAuthentication.cs" />
+ <Compile Include="src\tls\ServerSrpParams.cs" />
+ <Compile Include="src\tls\SessionParameters.cs" />
+ <Compile Include="src\tls\SignatureAlgorithm.cs" />
+ <Compile Include="src\tls\SignatureAndHashAlgorithm.cs" />
+ <Compile Include="src\tls\SignatureScheme.cs" />
+ <Compile Include="src\tls\SimulatedTlsSrpIdentityManager.cs" />
+ <Compile Include="src\tls\SrpTlsClient.cs" />
+ <Compile Include="src\tls\SrpTlsServer.cs" />
+ <Compile Include="src\tls\SrtpProtectionProfile.cs" />
+ <Compile Include="src\tls\Ssl3Utilities.cs" />
+ <Compile Include="src\tls\SupplementalDataEntry.cs" />
+ <Compile Include="src\tls\SupplementalDataType.cs" />
+ <Compile Include="src\tls\Timeout.cs" />
+ <Compile Include="src\tls\TlsAuthentication.cs" />
+ <Compile Include="src\tls\TlsClient.cs" />
+ <Compile Include="src\tls\TlsClientContext.cs" />
+ <Compile Include="src\tls\TlsClientContextImpl.cs" />
+ <Compile Include="src\tls\TlsClientProtocol.cs" />
+ <Compile Include="src\tls\TlsCloseable.cs" />
+ <Compile Include="src\tls\TlsContext.cs" />
+ <Compile Include="src\tls\TlsCredentialedAgreement.cs" />
+ <Compile Include="src\tls\TlsCredentialedDecryptor.cs" />
+ <Compile Include="src\tls\TlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\TlsCredentials.cs" />
+ <Compile Include="src\tls\TlsDHanonKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDheKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDHGroupVerifier.cs" />
+ <Compile Include="src\tls\TlsDHKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDHUtilities.cs" />
+ <Compile Include="src\tls\TlsEccUtilities.cs" />
+ <Compile Include="src\tls\TlsECDHanonKeyExchange.cs" />
+ <Compile Include="src\tls\TlsECDheKeyExchange.cs" />
+ <Compile Include="src\tls\TlsECDHKeyExchange.cs" />
+ <Compile Include="src\tls\TlsException.cs" />
+ <Compile Include="src\tls\TlsExtensionsUtilities.cs" />
+ <Compile Include="src\tls\TlsFatalAlert.cs" />
+ <Compile Include="src\tls\TlsFatalAlertReceived.cs" />
+ <Compile Include="src\tls\TlsHandshakeHash.cs" />
+ <Compile Include="src\tls\TlsHeartbeat.cs" />
+ <Compile Include="src\tls\TlsKeyExchange.cs" />
+ <Compile Include="src\tls\TlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\TlsNoCloseNotifyException.cs" />
+ <Compile Include="src\tls\TlsObjectIdentifiers.cs" />
+ <Compile Include="src\tls\TlsPeer.cs" />
+ <Compile Include="src\tls\TlsProtocol.cs" />
+ <Compile Include="src\tls\TlsPskIdentity.cs" />
+ <Compile Include="src\tls\TlsPskIdentityManager.cs" />
+ <Compile Include="src\tls\TlsPskKeyExchange.cs" />
+ <Compile Include="src\tls\TlsRsaKeyExchange.cs" />
+ <Compile Include="src\tls\TlsRsaUtilities.cs" />
+ <Compile Include="src\tls\TlsServer.cs" />
+ <Compile Include="src\tls\TlsServerCertificate.cs" />
+ <Compile Include="src\tls\TlsServerCertificateImpl.cs" />
+ <Compile Include="src\tls\TlsServerContext.cs" />
+ <Compile Include="src\tls\TlsServerContextImpl.cs" />
+ <Compile Include="src\tls\TlsServerProtocol.cs" />
+ <Compile Include="src\tls\TlsSession.cs" />
+ <Compile Include="src\tls\TlsSessionImpl.cs" />
+ <Compile Include="src\tls\TlsSrpConfigVerifier.cs" />
+ <Compile Include="src\tls\TlsSrpIdentity.cs" />
+ <Compile Include="src\tls\TlsSrpIdentityManager.cs" />
+ <Compile Include="src\tls\TlsSrpKeyExchange.cs" />
+ <Compile Include="src\tls\TlsSrpLoginParameters.cs" />
+ <Compile Include="src\tls\TlsSrpUtilities.cs" />
+ <Compile Include="src\tls\TlsSrtpUtilities.cs" />
+ <Compile Include="src\tls\TlsStream.cs" />
+ <Compile Include="src\tls\TlsTimeoutException.cs" />
+ <Compile Include="src\tls\TlsUtilities.cs" />
+ <Compile Include="src\tls\TrustedAuthority.cs" />
+ <Compile Include="src\tls\UrlAndHash.cs" />
+ <Compile Include="src\tls\UserMappingType.cs" />
+ <Compile Include="src\tls\UseSrtpData.cs" />
<Compile Include="src\tsp\GenTimeAccuracy.cs" />
<Compile Include="src\tsp\TSPAlgorithms.cs" />
<Compile Include="src\tsp\TSPException.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index 26db97e1b..f972a7694 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -1554,6 +1554,272 @@
<Compile Include="src\security\cert\CertificateNotYetValidException.cs" />
<Compile Include="src\security\cert\CertificateParsingException.cs" />
<Compile Include="src\security\cert\CrlException.cs" />
+ <Compile Include="src\tls\AbstractTlsClient.cs" />
+ <Compile Include="src\tls\AbstractTlsContext.cs" />
+ <Compile Include="src\tls\AbstractTlsKeyExchange.cs" />
+ <Compile Include="src\tls\AbstractTlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\AbstractTlsPeer.cs" />
+ <Compile Include="src\tls\AbstractTlsServer.cs" />
+ <Compile Include="src\tls\AlertDescription.cs" />
+ <Compile Include="src\tls\AlertLevel.cs" />
+ <Compile Include="src\tls\BasicTlsPskIdentity.cs" />
+ <Compile Include="src\tls\BasicTlsSrpIdentity.cs" />
+ <Compile Include="src\tls\ByteQueue.cs" />
+ <Compile Include="src\tls\ByteQueueInputStream.cs" />
+ <Compile Include="src\tls\ByteQueueOutputStream.cs" />
+ <Compile Include="src\tls\CachedInformationType.cs" />
+ <Compile Include="src\tls\CertChainType.cs" />
+ <Compile Include="src\tls\Certificate.cs" />
+ <Compile Include="src\tls\CertificateEntry.cs" />
+ <Compile Include="src\tls\CertificateRequest.cs" />
+ <Compile Include="src\tls\CertificateStatus.cs" />
+ <Compile Include="src\tls\CertificateStatusRequest.cs" />
+ <Compile Include="src\tls\CertificateStatusRequestItemV2.cs" />
+ <Compile Include="src\tls\CertificateStatusType.cs" />
+ <Compile Include="src\tls\CertificateType.cs" />
+ <Compile Include="src\tls\CertificateUrl.cs" />
+ <Compile Include="src\tls\ChangeCipherSpec.cs" />
+ <Compile Include="src\tls\ChannelBinding.cs" />
+ <Compile Include="src\tls\CipherSuite.cs" />
+ <Compile Include="src\tls\CipherType.cs" />
+ <Compile Include="src\tls\ClientAuthenticationType.cs" />
+ <Compile Include="src\tls\ClientCertificateType.cs" />
+ <Compile Include="src\tls\ClientHello.cs" />
+ <Compile Include="src\tls\CombinedHash.cs" />
+ <Compile Include="src\tls\CompressionMethod.cs" />
+ <Compile Include="src\tls\ConnectionEnd.cs" />
+ <Compile Include="src\tls\ContentType.cs" />
+ <Compile Include="src\tls\crypto\CryptoHashAlgorithm.cs" />
+ <Compile Include="src\tls\crypto\CryptoSignatureAlgorithm.cs" />
+ <Compile Include="src\tls\crypto\DHGroup.cs" />
+ <Compile Include="src\tls\crypto\DHStandardGroups.cs" />
+ <Compile Include="src\tls\crypto\impl\AbstractTlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\impl\AbstractTlsSecret.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcChaCha20Poly1305.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedAgreement.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedDecryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcSsl3Hmac.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsAeadCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsBlockCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsCertificate.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDH.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDHDomain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDssSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDssVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDH.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDomain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsa13Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsa13Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd25519Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd25519Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd448Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd448Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsHash.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsHmac.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsNonceGenerator.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaEncryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaPssSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaPssVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSecret.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6Client.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6Server.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6VerifierGenerator.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsStreamVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcVerifyingStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX25519.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX25519Domain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX448.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX448Domain.cs" />
+ <Compile Include="src\tls\crypto\impl\RsaUtilities.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsAeadCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsAeadCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsBlockCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsBlockCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsEncryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsImplUtilities.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsNullCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsSuiteHmac.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsSuiteMac.cs" />
+ <Compile Include="src\tls\crypto\Srp6Group.cs" />
+ <Compile Include="src\tls\crypto\Srp6StandardGroups.cs" />
+ <Compile Include="src\tls\crypto\TlsAgreement.cs" />
+ <Compile Include="src\tls\crypto\TlsCertificate.cs" />
+ <Compile Include="src\tls\crypto\TlsCertificateRole.cs" />
+ <Compile Include="src\tls\crypto\TlsCipher.cs" />
+ <Compile Include="src\tls\crypto\TlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoException.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoParameters.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoUtilities.cs" />
+ <Compile Include="src\tls\crypto\TlsDecodeResult.cs" />
+ <Compile Include="src\tls\crypto\TlsDHConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsDHDomain.cs" />
+ <Compile Include="src\tls\crypto\TlsECConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsECDomain.cs" />
+ <Compile Include="src\tls\crypto\TlsEncodeResult.cs" />
+ <Compile Include="src\tls\crypto\TlsHash.cs" />
+ <Compile Include="src\tls\crypto\TlsHashSink.cs" />
+ <Compile Include="src\tls\crypto\TlsHmac.cs" />
+ <Compile Include="src\tls\crypto\TlsMac.cs" />
+ <Compile Include="src\tls\crypto\TlsMacSink.cs" />
+ <Compile Include="src\tls\crypto\TlsNonceGenerator.cs" />
+ <Compile Include="src\tls\crypto\TlsNullNullCipher.cs" />
+ <Compile Include="src\tls\crypto\TlsSecret.cs" />
+ <Compile Include="src\tls\crypto\TlsSigner.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6Client.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6Server.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6VerifierGenerator.cs" />
+ <Compile Include="src\tls\crypto\TlsSrpConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\TlsStreamVerifier.cs" />
+ <Compile Include="src\tls\crypto\TlsVerifier.cs" />
+ <Compile Include="src\tls\DatagramReceiver.cs" />
+ <Compile Include="src\tls\DatagramSender.cs" />
+ <Compile Include="src\tls\DatagramTransport.cs" />
+ <Compile Include="src\tls\DefaultTlsClient.cs" />
+ <Compile Include="src\tls\DefaultTlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\DefaultTlsDHGroupVerifier.cs" />
+ <Compile Include="src\tls\DefaultTlsHeartbeat.cs" />
+ <Compile Include="src\tls\DefaultTlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\DefaultTlsServer.cs" />
+ <Compile Include="src\tls\DefaultTlsSrpConfigVerifier.cs" />
+ <Compile Include="src\tls\DeferredHash.cs" />
+ <Compile Include="src\tls\DigestInputBuffer.cs" />
+ <Compile Include="src\tls\DigitallySigned.cs" />
+ <Compile Include="src\tls\DtlsClientProtocol.cs" />
+ <Compile Include="src\tls\DtlsEpoch.cs" />
+ <Compile Include="src\tls\DtlsHandshakeRetransmit.cs" />
+ <Compile Include="src\tls\DtlsProtocol.cs" />
+ <Compile Include="src\tls\DtlsReassembler.cs" />
+ <Compile Include="src\tls\DtlsRecordLayer.cs" />
+ <Compile Include="src\tls\DtlsReliableHandshake.cs" />
+ <Compile Include="src\tls\DtlsReplayWindow.cs" />
+ <Compile Include="src\tls\DtlsRequest.cs" />
+ <Compile Include="src\tls\DtlsServerProtocol.cs" />
+ <Compile Include="src\tls\DtlsTransport.cs" />
+ <Compile Include="src\tls\DtlsVerifier.cs" />
+ <Compile Include="src\tls\ECCurveType.cs" />
+ <Compile Include="src\tls\ECPointFormat.cs" />
+ <Compile Include="src\tls\EncryptionAlgorithm.cs" />
+ <Compile Include="src\tls\ExporterLabel.cs" />
+ <Compile Include="src\tls\ExtensionType.cs" />
+ <Compile Include="src\tls\HandshakeMessageInput.cs" />
+ <Compile Include="src\tls\HandshakeMessageOutput.cs" />
+ <Compile Include="src\tls\HandshakeType.cs" />
+ <Compile Include="src\tls\HashAlgorithm.cs" />
+ <Compile Include="src\tls\HeartbeatExtension.cs" />
+ <Compile Include="src\tls\HeartbeatMessage.cs" />
+ <Compile Include="src\tls\HeartbeatMessageType.cs" />
+ <Compile Include="src\tls\HeartbeatMode.cs" />
+ <Compile Include="src\tls\IdentifierType.cs" />
+ <Compile Include="src\tls\KeyExchangeAlgorithm.cs" />
+ <Compile Include="src\tls\KeyShareEntry.cs" />
+ <Compile Include="src\tls\KeyUpdateRequest.cs" />
+ <Compile Include="src\tls\MacAlgorithm.cs" />
+ <Compile Include="src\tls\MaxFragmentLength.cs" />
+ <Compile Include="src\tls\NamedGroup.cs" />
+ <Compile Include="src\tls\NamedGroupRole.cs" />
+ <Compile Include="src\tls\NameType.cs" />
+ <Compile Include="src\tls\NewSessionTicket.cs" />
+ <Compile Include="src\tls\OcspStatusRequest.cs" />
+ <Compile Include="src\tls\OfferedPsks.cs" />
+ <Compile Include="src\tls\PrfAlgorithm.cs" />
+ <Compile Include="src\tls\ProtocolName.cs" />
+ <Compile Include="src\tls\ProtocolVersion.cs" />
+ <Compile Include="src\tls\PskIdentity.cs" />
+ <Compile Include="src\tls\PskKeyExchangeMode.cs" />
+ <Compile Include="src\tls\PskTlsClient.cs" />
+ <Compile Include="src\tls\PskTlsServer.cs" />
+ <Compile Include="src\tls\RecordFormat.cs" />
+ <Compile Include="src\tls\RecordPreview.cs" />
+ <Compile Include="src\tls\RecordStream.cs" />
+ <Compile Include="src\tls\SecurityParameters.cs" />
+ <Compile Include="src\tls\ServerHello.cs" />
+ <Compile Include="src\tls\ServerName.cs" />
+ <Compile Include="src\tls\ServerNameList.cs" />
+ <Compile Include="src\tls\ServerOnlyTlsAuthentication.cs" />
+ <Compile Include="src\tls\ServerSrpParams.cs" />
+ <Compile Include="src\tls\SessionParameters.cs" />
+ <Compile Include="src\tls\SignatureAlgorithm.cs" />
+ <Compile Include="src\tls\SignatureAndHashAlgorithm.cs" />
+ <Compile Include="src\tls\SignatureScheme.cs" />
+ <Compile Include="src\tls\SimulatedTlsSrpIdentityManager.cs" />
+ <Compile Include="src\tls\SrpTlsClient.cs" />
+ <Compile Include="src\tls\SrpTlsServer.cs" />
+ <Compile Include="src\tls\SrtpProtectionProfile.cs" />
+ <Compile Include="src\tls\Ssl3Utilities.cs" />
+ <Compile Include="src\tls\SupplementalDataEntry.cs" />
+ <Compile Include="src\tls\SupplementalDataType.cs" />
+ <Compile Include="src\tls\Timeout.cs" />
+ <Compile Include="src\tls\TlsAuthentication.cs" />
+ <Compile Include="src\tls\TlsClient.cs" />
+ <Compile Include="src\tls\TlsClientContext.cs" />
+ <Compile Include="src\tls\TlsClientContextImpl.cs" />
+ <Compile Include="src\tls\TlsClientProtocol.cs" />
+ <Compile Include="src\tls\TlsCloseable.cs" />
+ <Compile Include="src\tls\TlsContext.cs" />
+ <Compile Include="src\tls\TlsCredentialedAgreement.cs" />
+ <Compile Include="src\tls\TlsCredentialedDecryptor.cs" />
+ <Compile Include="src\tls\TlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\TlsCredentials.cs" />
+ <Compile Include="src\tls\TlsDHanonKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDheKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDHGroupVerifier.cs" />
+ <Compile Include="src\tls\TlsDHKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDHUtilities.cs" />
+ <Compile Include="src\tls\TlsEccUtilities.cs" />
+ <Compile Include="src\tls\TlsECDHanonKeyExchange.cs" />
+ <Compile Include="src\tls\TlsECDheKeyExchange.cs" />
+ <Compile Include="src\tls\TlsECDHKeyExchange.cs" />
+ <Compile Include="src\tls\TlsException.cs" />
+ <Compile Include="src\tls\TlsExtensionsUtilities.cs" />
+ <Compile Include="src\tls\TlsFatalAlert.cs" />
+ <Compile Include="src\tls\TlsFatalAlertReceived.cs" />
+ <Compile Include="src\tls\TlsHandshakeHash.cs" />
+ <Compile Include="src\tls\TlsHeartbeat.cs" />
+ <Compile Include="src\tls\TlsKeyExchange.cs" />
+ <Compile Include="src\tls\TlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\TlsNoCloseNotifyException.cs" />
+ <Compile Include="src\tls\TlsObjectIdentifiers.cs" />
+ <Compile Include="src\tls\TlsPeer.cs" />
+ <Compile Include="src\tls\TlsProtocol.cs" />
+ <Compile Include="src\tls\TlsPskIdentity.cs" />
+ <Compile Include="src\tls\TlsPskIdentityManager.cs" />
+ <Compile Include="src\tls\TlsPskKeyExchange.cs" />
+ <Compile Include="src\tls\TlsRsaKeyExchange.cs" />
+ <Compile Include="src\tls\TlsRsaUtilities.cs" />
+ <Compile Include="src\tls\TlsServer.cs" />
+ <Compile Include="src\tls\TlsServerCertificate.cs" />
+ <Compile Include="src\tls\TlsServerCertificateImpl.cs" />
+ <Compile Include="src\tls\TlsServerContext.cs" />
+ <Compile Include="src\tls\TlsServerContextImpl.cs" />
+ <Compile Include="src\tls\TlsServerProtocol.cs" />
+ <Compile Include="src\tls\TlsSession.cs" />
+ <Compile Include="src\tls\TlsSessionImpl.cs" />
+ <Compile Include="src\tls\TlsSrpConfigVerifier.cs" />
+ <Compile Include="src\tls\TlsSrpIdentity.cs" />
+ <Compile Include="src\tls\TlsSrpIdentityManager.cs" />
+ <Compile Include="src\tls\TlsSrpKeyExchange.cs" />
+ <Compile Include="src\tls\TlsSrpLoginParameters.cs" />
+ <Compile Include="src\tls\TlsSrpUtilities.cs" />
+ <Compile Include="src\tls\TlsSrtpUtilities.cs" />
+ <Compile Include="src\tls\TlsStream.cs" />
+ <Compile Include="src\tls\TlsTimeoutException.cs" />
+ <Compile Include="src\tls\TlsUtilities.cs" />
+ <Compile Include="src\tls\TrustedAuthority.cs" />
+ <Compile Include="src\tls\UrlAndHash.cs" />
+ <Compile Include="src\tls\UserMappingType.cs" />
+ <Compile Include="src\tls\UseSrtpData.cs" />
<Compile Include="src\tsp\GenTimeAccuracy.cs" />
<Compile Include="src\tsp\TSPAlgorithms.cs" />
<Compile Include="src\tsp\TSPException.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 17fe3dda5..682b74d5e 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -1555,6 +1555,272 @@
<Compile Include="src\security\cert\CertificateNotYetValidException.cs" />
<Compile Include="src\security\cert\CertificateParsingException.cs" />
<Compile Include="src\security\cert\CrlException.cs" />
+ <Compile Include="src\tls\AbstractTlsClient.cs" />
+ <Compile Include="src\tls\AbstractTlsContext.cs" />
+ <Compile Include="src\tls\AbstractTlsKeyExchange.cs" />
+ <Compile Include="src\tls\AbstractTlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\AbstractTlsPeer.cs" />
+ <Compile Include="src\tls\AbstractTlsServer.cs" />
+ <Compile Include="src\tls\AlertDescription.cs" />
+ <Compile Include="src\tls\AlertLevel.cs" />
+ <Compile Include="src\tls\BasicTlsPskIdentity.cs" />
+ <Compile Include="src\tls\BasicTlsSrpIdentity.cs" />
+ <Compile Include="src\tls\ByteQueue.cs" />
+ <Compile Include="src\tls\ByteQueueInputStream.cs" />
+ <Compile Include="src\tls\ByteQueueOutputStream.cs" />
+ <Compile Include="src\tls\CachedInformationType.cs" />
+ <Compile Include="src\tls\CertChainType.cs" />
+ <Compile Include="src\tls\Certificate.cs" />
+ <Compile Include="src\tls\CertificateEntry.cs" />
+ <Compile Include="src\tls\CertificateRequest.cs" />
+ <Compile Include="src\tls\CertificateStatus.cs" />
+ <Compile Include="src\tls\CertificateStatusRequest.cs" />
+ <Compile Include="src\tls\CertificateStatusRequestItemV2.cs" />
+ <Compile Include="src\tls\CertificateStatusType.cs" />
+ <Compile Include="src\tls\CertificateType.cs" />
+ <Compile Include="src\tls\CertificateUrl.cs" />
+ <Compile Include="src\tls\ChangeCipherSpec.cs" />
+ <Compile Include="src\tls\ChannelBinding.cs" />
+ <Compile Include="src\tls\CipherSuite.cs" />
+ <Compile Include="src\tls\CipherType.cs" />
+ <Compile Include="src\tls\ClientAuthenticationType.cs" />
+ <Compile Include="src\tls\ClientCertificateType.cs" />
+ <Compile Include="src\tls\ClientHello.cs" />
+ <Compile Include="src\tls\CombinedHash.cs" />
+ <Compile Include="src\tls\CompressionMethod.cs" />
+ <Compile Include="src\tls\ConnectionEnd.cs" />
+ <Compile Include="src\tls\ContentType.cs" />
+ <Compile Include="src\tls\crypto\CryptoHashAlgorithm.cs" />
+ <Compile Include="src\tls\crypto\CryptoSignatureAlgorithm.cs" />
+ <Compile Include="src\tls\crypto\DHGroup.cs" />
+ <Compile Include="src\tls\crypto\DHStandardGroups.cs" />
+ <Compile Include="src\tls\crypto\impl\AbstractTlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\impl\AbstractTlsSecret.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcChaCha20Poly1305.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedAgreement.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedDecryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcDefaultTlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcSsl3Hmac.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsAeadCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsBlockCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsCertificate.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDH.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDHDomain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDssSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsDssVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDH.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDomain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsa13Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsa13Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsECDsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd25519Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd25519Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd448Signer.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsEd448Verifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsHash.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsHmac.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsNonceGenerator.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaEncryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaPssSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaPssVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsRsaVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSecret.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6Client.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6Server.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsSrp6VerifierGenerator.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsStreamVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcTlsVerifier.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcVerifyingStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX25519.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX25519Domain.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX448.cs" />
+ <Compile Include="src\tls\crypto\impl\bc\BcX448Domain.cs" />
+ <Compile Include="src\tls\crypto\impl\RsaUtilities.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsAeadCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsAeadCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsBlockCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsBlockCipherImpl.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsEncryptor.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsImplUtilities.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsNullCipher.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsSuiteHmac.cs" />
+ <Compile Include="src\tls\crypto\impl\TlsSuiteMac.cs" />
+ <Compile Include="src\tls\crypto\Srp6Group.cs" />
+ <Compile Include="src\tls\crypto\Srp6StandardGroups.cs" />
+ <Compile Include="src\tls\crypto\TlsAgreement.cs" />
+ <Compile Include="src\tls\crypto\TlsCertificate.cs" />
+ <Compile Include="src\tls\crypto\TlsCertificateRole.cs" />
+ <Compile Include="src\tls\crypto\TlsCipher.cs" />
+ <Compile Include="src\tls\crypto\TlsCrypto.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoException.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoParameters.cs" />
+ <Compile Include="src\tls\crypto\TlsCryptoUtilities.cs" />
+ <Compile Include="src\tls\crypto\TlsDecodeResult.cs" />
+ <Compile Include="src\tls\crypto\TlsDHConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsDHDomain.cs" />
+ <Compile Include="src\tls\crypto\TlsECConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsECDomain.cs" />
+ <Compile Include="src\tls\crypto\TlsEncodeResult.cs" />
+ <Compile Include="src\tls\crypto\TlsHash.cs" />
+ <Compile Include="src\tls\crypto\TlsHashSink.cs" />
+ <Compile Include="src\tls\crypto\TlsHmac.cs" />
+ <Compile Include="src\tls\crypto\TlsMac.cs" />
+ <Compile Include="src\tls\crypto\TlsMacSink.cs" />
+ <Compile Include="src\tls\crypto\TlsNonceGenerator.cs" />
+ <Compile Include="src\tls\crypto\TlsNullNullCipher.cs" />
+ <Compile Include="src\tls\crypto\TlsSecret.cs" />
+ <Compile Include="src\tls\crypto\TlsSigner.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6Client.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6Server.cs" />
+ <Compile Include="src\tls\crypto\TlsSrp6VerifierGenerator.cs" />
+ <Compile Include="src\tls\crypto\TlsSrpConfig.cs" />
+ <Compile Include="src\tls\crypto\TlsStreamSigner.cs" />
+ <Compile Include="src\tls\crypto\TlsStreamVerifier.cs" />
+ <Compile Include="src\tls\crypto\TlsVerifier.cs" />
+ <Compile Include="src\tls\DatagramReceiver.cs" />
+ <Compile Include="src\tls\DatagramSender.cs" />
+ <Compile Include="src\tls\DatagramTransport.cs" />
+ <Compile Include="src\tls\DefaultTlsClient.cs" />
+ <Compile Include="src\tls\DefaultTlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\DefaultTlsDHGroupVerifier.cs" />
+ <Compile Include="src\tls\DefaultTlsHeartbeat.cs" />
+ <Compile Include="src\tls\DefaultTlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\DefaultTlsServer.cs" />
+ <Compile Include="src\tls\DefaultTlsSrpConfigVerifier.cs" />
+ <Compile Include="src\tls\DeferredHash.cs" />
+ <Compile Include="src\tls\DigestInputBuffer.cs" />
+ <Compile Include="src\tls\DigitallySigned.cs" />
+ <Compile Include="src\tls\DtlsClientProtocol.cs" />
+ <Compile Include="src\tls\DtlsEpoch.cs" />
+ <Compile Include="src\tls\DtlsHandshakeRetransmit.cs" />
+ <Compile Include="src\tls\DtlsProtocol.cs" />
+ <Compile Include="src\tls\DtlsReassembler.cs" />
+ <Compile Include="src\tls\DtlsRecordLayer.cs" />
+ <Compile Include="src\tls\DtlsReliableHandshake.cs" />
+ <Compile Include="src\tls\DtlsReplayWindow.cs" />
+ <Compile Include="src\tls\DtlsRequest.cs" />
+ <Compile Include="src\tls\DtlsServerProtocol.cs" />
+ <Compile Include="src\tls\DtlsTransport.cs" />
+ <Compile Include="src\tls\DtlsVerifier.cs" />
+ <Compile Include="src\tls\ECCurveType.cs" />
+ <Compile Include="src\tls\ECPointFormat.cs" />
+ <Compile Include="src\tls\EncryptionAlgorithm.cs" />
+ <Compile Include="src\tls\ExporterLabel.cs" />
+ <Compile Include="src\tls\ExtensionType.cs" />
+ <Compile Include="src\tls\HandshakeMessageInput.cs" />
+ <Compile Include="src\tls\HandshakeMessageOutput.cs" />
+ <Compile Include="src\tls\HandshakeType.cs" />
+ <Compile Include="src\tls\HashAlgorithm.cs" />
+ <Compile Include="src\tls\HeartbeatExtension.cs" />
+ <Compile Include="src\tls\HeartbeatMessage.cs" />
+ <Compile Include="src\tls\HeartbeatMessageType.cs" />
+ <Compile Include="src\tls\HeartbeatMode.cs" />
+ <Compile Include="src\tls\IdentifierType.cs" />
+ <Compile Include="src\tls\KeyExchangeAlgorithm.cs" />
+ <Compile Include="src\tls\KeyShareEntry.cs" />
+ <Compile Include="src\tls\KeyUpdateRequest.cs" />
+ <Compile Include="src\tls\MacAlgorithm.cs" />
+ <Compile Include="src\tls\MaxFragmentLength.cs" />
+ <Compile Include="src\tls\NamedGroup.cs" />
+ <Compile Include="src\tls\NamedGroupRole.cs" />
+ <Compile Include="src\tls\NameType.cs" />
+ <Compile Include="src\tls\NewSessionTicket.cs" />
+ <Compile Include="src\tls\OcspStatusRequest.cs" />
+ <Compile Include="src\tls\OfferedPsks.cs" />
+ <Compile Include="src\tls\PrfAlgorithm.cs" />
+ <Compile Include="src\tls\ProtocolName.cs" />
+ <Compile Include="src\tls\ProtocolVersion.cs" />
+ <Compile Include="src\tls\PskIdentity.cs" />
+ <Compile Include="src\tls\PskKeyExchangeMode.cs" />
+ <Compile Include="src\tls\PskTlsClient.cs" />
+ <Compile Include="src\tls\PskTlsServer.cs" />
+ <Compile Include="src\tls\RecordFormat.cs" />
+ <Compile Include="src\tls\RecordPreview.cs" />
+ <Compile Include="src\tls\RecordStream.cs" />
+ <Compile Include="src\tls\SecurityParameters.cs" />
+ <Compile Include="src\tls\ServerHello.cs" />
+ <Compile Include="src\tls\ServerName.cs" />
+ <Compile Include="src\tls\ServerNameList.cs" />
+ <Compile Include="src\tls\ServerOnlyTlsAuthentication.cs" />
+ <Compile Include="src\tls\ServerSrpParams.cs" />
+ <Compile Include="src\tls\SessionParameters.cs" />
+ <Compile Include="src\tls\SignatureAlgorithm.cs" />
+ <Compile Include="src\tls\SignatureAndHashAlgorithm.cs" />
+ <Compile Include="src\tls\SignatureScheme.cs" />
+ <Compile Include="src\tls\SimulatedTlsSrpIdentityManager.cs" />
+ <Compile Include="src\tls\SrpTlsClient.cs" />
+ <Compile Include="src\tls\SrpTlsServer.cs" />
+ <Compile Include="src\tls\SrtpProtectionProfile.cs" />
+ <Compile Include="src\tls\Ssl3Utilities.cs" />
+ <Compile Include="src\tls\SupplementalDataEntry.cs" />
+ <Compile Include="src\tls\SupplementalDataType.cs" />
+ <Compile Include="src\tls\Timeout.cs" />
+ <Compile Include="src\tls\TlsAuthentication.cs" />
+ <Compile Include="src\tls\TlsClient.cs" />
+ <Compile Include="src\tls\TlsClientContext.cs" />
+ <Compile Include="src\tls\TlsClientContextImpl.cs" />
+ <Compile Include="src\tls\TlsClientProtocol.cs" />
+ <Compile Include="src\tls\TlsCloseable.cs" />
+ <Compile Include="src\tls\TlsContext.cs" />
+ <Compile Include="src\tls\TlsCredentialedAgreement.cs" />
+ <Compile Include="src\tls\TlsCredentialedDecryptor.cs" />
+ <Compile Include="src\tls\TlsCredentialedSigner.cs" />
+ <Compile Include="src\tls\TlsCredentials.cs" />
+ <Compile Include="src\tls\TlsDHanonKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDheKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDHGroupVerifier.cs" />
+ <Compile Include="src\tls\TlsDHKeyExchange.cs" />
+ <Compile Include="src\tls\TlsDHUtilities.cs" />
+ <Compile Include="src\tls\TlsEccUtilities.cs" />
+ <Compile Include="src\tls\TlsECDHanonKeyExchange.cs" />
+ <Compile Include="src\tls\TlsECDheKeyExchange.cs" />
+ <Compile Include="src\tls\TlsECDHKeyExchange.cs" />
+ <Compile Include="src\tls\TlsException.cs" />
+ <Compile Include="src\tls\TlsExtensionsUtilities.cs" />
+ <Compile Include="src\tls\TlsFatalAlert.cs" />
+ <Compile Include="src\tls\TlsFatalAlertReceived.cs" />
+ <Compile Include="src\tls\TlsHandshakeHash.cs" />
+ <Compile Include="src\tls\TlsHeartbeat.cs" />
+ <Compile Include="src\tls\TlsKeyExchange.cs" />
+ <Compile Include="src\tls\TlsKeyExchangeFactory.cs" />
+ <Compile Include="src\tls\TlsNoCloseNotifyException.cs" />
+ <Compile Include="src\tls\TlsObjectIdentifiers.cs" />
+ <Compile Include="src\tls\TlsPeer.cs" />
+ <Compile Include="src\tls\TlsProtocol.cs" />
+ <Compile Include="src\tls\TlsPskIdentity.cs" />
+ <Compile Include="src\tls\TlsPskIdentityManager.cs" />
+ <Compile Include="src\tls\TlsPskKeyExchange.cs" />
+ <Compile Include="src\tls\TlsRsaKeyExchange.cs" />
+ <Compile Include="src\tls\TlsRsaUtilities.cs" />
+ <Compile Include="src\tls\TlsServer.cs" />
+ <Compile Include="src\tls\TlsServerCertificate.cs" />
+ <Compile Include="src\tls\TlsServerCertificateImpl.cs" />
+ <Compile Include="src\tls\TlsServerContext.cs" />
+ <Compile Include="src\tls\TlsServerContextImpl.cs" />
+ <Compile Include="src\tls\TlsServerProtocol.cs" />
+ <Compile Include="src\tls\TlsSession.cs" />
+ <Compile Include="src\tls\TlsSessionImpl.cs" />
+ <Compile Include="src\tls\TlsSrpConfigVerifier.cs" />
+ <Compile Include="src\tls\TlsSrpIdentity.cs" />
+ <Compile Include="src\tls\TlsSrpIdentityManager.cs" />
+ <Compile Include="src\tls\TlsSrpKeyExchange.cs" />
+ <Compile Include="src\tls\TlsSrpLoginParameters.cs" />
+ <Compile Include="src\tls\TlsSrpUtilities.cs" />
+ <Compile Include="src\tls\TlsSrtpUtilities.cs" />
+ <Compile Include="src\tls\TlsStream.cs" />
+ <Compile Include="src\tls\TlsTimeoutException.cs" />
+ <Compile Include="src\tls\TlsUtilities.cs" />
+ <Compile Include="src\tls\TrustedAuthority.cs" />
+ <Compile Include="src\tls\UrlAndHash.cs" />
+ <Compile Include="src\tls\UserMappingType.cs" />
+ <Compile Include="src\tls\UseSrtpData.cs" />
<Compile Include="src\tsp\GenTimeAccuracy.cs" />
<Compile Include="src\tsp\TSPAlgorithms.cs" />
<Compile Include="src\tsp\TSPException.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 1ba78a83c..97fcb3a5b 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -7659,6 +7659,1336 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\tls\AbstractTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AbstractTlsContext.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AbstractTlsKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AbstractTlsKeyExchangeFactory.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AbstractTlsPeer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AbstractTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AlertDescription.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\AlertLevel.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\BasicTlsPskIdentity.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\BasicTlsSrpIdentity.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ByteQueue.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ByteQueueInputStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ByteQueueOutputStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CachedInformationType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertChainType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\Certificate.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateEntry.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateRequest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateStatus.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateStatusRequest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateStatusRequestItemV2.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateStatusType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CertificateUrl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ChangeCipherSpec.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ChannelBinding.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CipherSuite.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CipherType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ClientAuthenticationType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ClientCertificateType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ClientHello.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CombinedHash.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\CompressionMethod.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ConnectionEnd.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ContentType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\CryptoHashAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\CryptoSignatureAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\DHGroup.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\DHStandardGroups.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\AbstractTlsCrypto.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\AbstractTlsSecret.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcChaCha20Poly1305.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcDefaultTlsCredentialedAgreement.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcDefaultTlsCredentialedDecryptor.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcDefaultTlsCredentialedSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcSsl3Hmac.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsAeadCipherImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsBlockCipherImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsCertificate.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsCrypto.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsDH.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsDHDomain.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsDsaSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsDsaVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsDssSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsDssVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsECDH.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsECDomain.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsECDsa13Signer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsECDsa13Verifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsECDsaSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsECDsaVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsEd25519Signer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsEd25519Verifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsEd448Signer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsEd448Verifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsHash.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsHmac.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsNonceGenerator.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsRsaEncryptor.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsRsaPssSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsRsaPssVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsRsaSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsRsaVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsSecret.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsSrp6Client.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsSrp6Server.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsSrp6VerifierGenerator.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsStreamSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsStreamVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcTlsVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcVerifyingStreamSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcX25519.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcX25519Domain.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcX448.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\bc\BcX448Domain.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\RsaUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsAeadCipher.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsAeadCipherImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsBlockCipher.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsBlockCipherImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsEncryptor.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsImplUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsNullCipher.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsSuiteHmac.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\impl\TlsSuiteMac.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\Srp6Group.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\Srp6StandardGroups.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsAgreement.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCertificate.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCertificateRole.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCipher.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCrypto.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCryptoException.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCryptoParameters.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsCryptoUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsDecodeResult.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsDHConfig.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsDHDomain.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsECConfig.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsECDomain.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsEncodeResult.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsHash.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsHashSink.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsHmac.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsMac.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsMacSink.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsNonceGenerator.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsNullNullCipher.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsSecret.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsSrp6Client.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsSrp6Server.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsSrp6VerifierGenerator.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsSrpConfig.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsStreamSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsStreamVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\crypto\TlsVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DatagramReceiver.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DatagramSender.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DatagramTransport.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsCredentialedSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsDHGroupVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsHeartbeat.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsKeyExchangeFactory.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DefaultTlsSrpConfigVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DeferredHash.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DigestInputBuffer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DigitallySigned.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsClientProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsEpoch.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsHandshakeRetransmit.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsReassembler.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsRecordLayer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsReliableHandshake.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsReplayWindow.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsRequest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsServerProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsTransport.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\DtlsVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ECCurveType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ECPointFormat.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\EncryptionAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ExporterLabel.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ExtensionType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HandshakeMessageInput.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HandshakeMessageOutput.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HandshakeType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HashAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HeartbeatExtension.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HeartbeatMessage.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HeartbeatMessageType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\HeartbeatMode.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\IdentifierType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\KeyExchangeAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\KeyShareEntry.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\KeyUpdateRequest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\MacAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\MaxFragmentLength.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\NamedGroup.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\NamedGroupRole.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\NameType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\NewSessionTicket.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\OcspStatusRequest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\OfferedPsks.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\PrfAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ProtocolName.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ProtocolVersion.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\PskIdentity.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\PskKeyExchangeMode.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\PskTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\PskTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\RecordFormat.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\RecordPreview.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\RecordStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SecurityParameters.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ServerHello.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ServerName.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ServerNameList.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ServerOnlyTlsAuthentication.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\ServerSrpParams.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SessionParameters.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SignatureAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SignatureAndHashAlgorithm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SignatureScheme.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SimulatedTlsSrpIdentityManager.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SrpTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SrpTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SrtpProtectionProfile.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\Ssl3Utilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SupplementalDataEntry.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\SupplementalDataType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\Timeout.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsAuthentication.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsClientContext.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsClientContextImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsClientProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsCloseable.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsContext.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsCredentialedAgreement.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsCredentialedDecryptor.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsCredentialedSigner.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsCredentials.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsDHanonKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsDheKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsDHGroupVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsDHKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsDHUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsEccUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsECDHanonKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsECDheKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsECDHKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsException.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsExtensionsUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsFatalAlert.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsFatalAlertReceived.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsHandshakeHash.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsHeartbeat.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsKeyExchangeFactory.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsNoCloseNotifyException.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsObjectIdentifiers.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsPeer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsPskIdentity.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsPskIdentityManager.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsPskKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsRsaKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsRsaUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsServerCertificate.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsServerCertificateImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsServerContext.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsServerContextImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsServerProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSession.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSessionImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrpConfigVerifier.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrpIdentity.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrpIdentityManager.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrpKeyExchange.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrpLoginParameters.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrpUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsSrtpUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsTimeoutException.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TlsUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\TrustedAuthority.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\UrlAndHash.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\UserMappingType.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\tls\UseSrtpData.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\tsp\GenTimeAccuracy.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -13969,6 +15299,216 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "test\src\tls\crypto\test\BcTlsCryptoTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\ByteQueueInputStreamTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\CertChainUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\DtlsProtocolTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\DtlsPskProtocolTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\DtlsTestCase.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\DtlsTestClientProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\DtlsTestServerProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\DtlsTestSuite.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\LoggingDatagramTransport.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockDatagramAssociation.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockDtlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockDtlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockPskDtlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockPskDtlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockPskTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockPskTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockSrpTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockSrpTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\MockTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\NetworkStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\PipedStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\PrfTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\PskTlsClientTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\PskTlsServerTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsClientTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsProtocolNonBlockingTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsProtocolTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsPskProtocolTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsServerTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsSrpProtocolTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestCase.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestClientImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestClientProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestConfig.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestServerImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestServerProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestSuite.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsTestUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\TlsUtilitiesTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\tls\test\UnreliableDatagramTransport.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "test\src\tsp\test\AllTests.cs"
SubType = "Code"
BuildAction = "Compile"
diff --git a/crypto/src/tls/AbstractTlsClient.cs b/crypto/src/tls/AbstractTlsClient.cs
new file mode 100644
index 000000000..0a9410cd9
--- /dev/null
+++ b/crypto/src/tls/AbstractTlsClient.cs
@@ -0,0 +1,435 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base class for a TLS client.</summary>
+ public abstract class AbstractTlsClient
+ : AbstractTlsPeer, TlsClient
+ {
+ protected TlsClientContext m_context;
+ protected ProtocolVersion[] m_protocolVersions;
+ protected int[] m_cipherSuites;
+
+ protected IList m_supportedGroups;
+ protected IList m_supportedSignatureAlgorithms;
+ protected IList m_supportedSignatureAlgorithmsCert;
+
+ protected AbstractTlsClient(TlsCrypto crypto)
+ : base(crypto)
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual bool AllowUnexpectedServerExtension(int extensionType, byte[] extensionData)
+ {
+ switch (extensionType)
+ {
+ case ExtensionType.supported_groups:
+ /*
+ * 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.
+ */
+ TlsExtensionsUtilities.ReadSupportedGroupsExtension(extensionData);
+ return true;
+
+ case ExtensionType.ec_point_formats:
+ /*
+ * Exception added based on field reports that some servers send this even when they
+ * didn't negotiate an ECC cipher suite. If present, we still require that it is a valid
+ * ECPointFormatList.
+ */
+ TlsExtensionsUtilities.ReadSupportedPointFormatsExtension(extensionData);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ protected virtual IList GetNamedGroupRoles()
+ {
+ IList namedGroupRoles = TlsUtilities.GetNamedGroupRoles(GetCipherSuites());
+ IList sigAlgs = m_supportedSignatureAlgorithms, sigAlgsCert = m_supportedSignatureAlgorithmsCert;
+
+ if ((null == sigAlgs || TlsUtilities.ContainsAnySignatureAlgorithm(sigAlgs, SignatureAlgorithm.ecdsa))
+ || (null != sigAlgsCert
+ && TlsUtilities.ContainsAnySignatureAlgorithm(sigAlgsCert, SignatureAlgorithm.ecdsa)))
+ {
+ TlsUtilities.AddToSet(namedGroupRoles, NamedGroupRole.ecdsa);
+ }
+
+ return namedGroupRoles;
+ }
+
+ /// <exception cref="IOException"/>
+ 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);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsPskIdentity GetPskIdentity()
+ {
+ return null;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsSrpIdentity GetSrpIdentity()
+ {
+ return null;
+ }
+
+ public virtual TlsDHGroupVerifier GetDHGroupVerifier()
+ {
+ return new DefaultTlsDHGroupVerifier();
+ }
+
+ public virtual TlsSrpConfigVerifier GetSrpConfigVerifier()
+ {
+ return new DefaultTlsSrpConfigVerifier();
+ }
+
+ protected virtual IList GetCertificateAuthorities()
+ {
+ return null;
+ }
+
+ protected virtual IList GetProtocolNames()
+ {
+ return null;
+ }
+
+ protected virtual CertificateStatusRequest GetCertificateStatusRequest()
+ {
+ return new CertificateStatusRequest(CertificateStatusType.ocsp, new OcspStatusRequest(null, null));
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="CertificateStatusRequestItemV2"/> (or null).</returns>
+ protected virtual IList GetMultiCertStatusRequest()
+ {
+ return null;
+ }
+
+ protected virtual IList GetSniServerNames()
+ {
+ return null;
+ }
+
+ /// <summary>The default <see cref="GetClientExtensions"/> implementation calls this to determine which named
+ /// groups to include in the supported_groups extension for the ClientHello.</summary>
+ /// <param name="namedGroupRoles">The <see cref="NamedGroupRole">named group roles</see> for which there should
+ /// be at least one supported group. By default this is inferred from the offered cipher suites and signature
+ /// algorithms.</param>
+ /// <returns>an <see cref="IList"/> of <see cref="Int32"/>. See <see cref="NamedGroup"/> for group constants.
+ /// </returns>
+ protected virtual IList GetSupportedGroups(IList namedGroupRoles)
+ {
+ TlsCrypto crypto = Crypto;
+ IList supportedGroups = Platform.CreateArrayList();
+
+ if (namedGroupRoles.Contains(NamedGroupRole.ecdh))
+ {
+ TlsUtilities.AddIfSupported(supportedGroups, crypto,
+ new int[]{ NamedGroup.x25519, NamedGroup.x448 });
+ }
+
+ if (namedGroupRoles.Contains(NamedGroupRole.ecdh) ||
+ namedGroupRoles.Contains(NamedGroupRole.ecdsa))
+ {
+ TlsUtilities.AddIfSupported(supportedGroups, crypto,
+ new int[]{ NamedGroup.secp256r1, NamedGroup.secp384r1 });
+ }
+
+ if (namedGroupRoles.Contains(NamedGroupRole.dh))
+ {
+ TlsUtilities.AddIfSupported(supportedGroups, crypto,
+ new int[]{ NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096 });
+ }
+
+ return supportedGroups;
+ }
+
+ protected virtual IList GetSupportedSignatureAlgorithms()
+ {
+ return TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+
+ protected virtual IList GetSupportedSignatureAlgorithmsCert()
+ {
+ return null;
+ }
+
+ protected virtual IList GetTrustedCAIndication()
+ {
+ return null;
+ }
+
+ public virtual void Init(TlsClientContext context)
+ {
+ this.m_context = context;
+
+ this.m_protocolVersions = GetSupportedVersions();
+ this.m_cipherSuites = GetSupportedCipherSuites();
+ }
+
+ public override ProtocolVersion[] GetProtocolVersions()
+ {
+ return m_protocolVersions;
+ }
+
+ public override int[] GetCipherSuites()
+ {
+ return m_cipherSuites;
+ }
+
+ /// <exception cref="IOException"/>
+ public override void NotifyHandshakeBeginning()
+ {
+ base.NotifyHandshakeBeginning();
+
+ this.m_supportedGroups = null;
+ this.m_supportedSignatureAlgorithms = null;
+ this.m_supportedSignatureAlgorithmsCert = null;
+ }
+
+ public virtual TlsSession GetSessionToResume()
+ {
+ return null;
+ }
+
+ public virtual bool IsFallback()
+ {
+ /*
+ * RFC 7507 4. The TLS_FALLBACK_SCSV cipher suite value is meant for use by clients that
+ * repeat a connection attempt with a downgraded protocol (perform a "fallback retry") in
+ * order to work around interoperability problems with legacy servers.
+ */
+ return false;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = Platform.CreateHashtable();
+
+ bool offeringTlsV13Plus = false;
+ bool offeringPreTlsV13 = false;
+ {
+ ProtocolVersion[] supportedVersions = GetProtocolVersions();
+ for (int i = 0; i < supportedVersions.Length; ++i)
+ {
+ if (TlsUtilities.IsTlsV13(supportedVersions[i]))
+ {
+ offeringTlsV13Plus = true;
+ }
+ else
+ {
+ offeringPreTlsV13 = true;
+ }
+ }
+ }
+
+ IList protocolNames = GetProtocolNames();
+ if (protocolNames != null)
+ {
+ TlsExtensionsUtilities.AddAlpnExtensionClient(clientExtensions, protocolNames);
+ }
+
+ IList sniServerNames = GetSniServerNames();
+ if (sniServerNames != null)
+ {
+ TlsExtensionsUtilities.AddServerNameExtensionClient(clientExtensions, sniServerNames);
+ }
+
+ CertificateStatusRequest statusRequest = GetCertificateStatusRequest();
+ if (statusRequest != null)
+ {
+ TlsExtensionsUtilities.AddStatusRequestExtension(clientExtensions, statusRequest);
+ }
+
+ if (offeringTlsV13Plus)
+ {
+ IList certificateAuthorities = GetCertificateAuthorities();
+ if (certificateAuthorities != null)
+ {
+ TlsExtensionsUtilities.AddCertificateAuthoritiesExtension(clientExtensions, certificateAuthorities);
+ }
+ }
+
+ if (offeringPreTlsV13)
+ {
+ // TODO Shouldn't add if no offered cipher suite uses a block cipher?
+ TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+
+ IList statusRequestV2 = GetMultiCertStatusRequest();
+ if (statusRequestV2 != null)
+ {
+ TlsExtensionsUtilities.AddStatusRequestV2Extension(clientExtensions, statusRequestV2);
+ }
+
+ IList trustedCAKeys = GetTrustedCAIndication();
+ if (trustedCAKeys != null)
+ {
+ TlsExtensionsUtilities.AddTrustedCAKeysExtensionClient(clientExtensions, trustedCAKeys);
+ }
+ }
+
+ ProtocolVersion clientVersion = m_context.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))
+ {
+ IList supportedSigAlgs = GetSupportedSignatureAlgorithms();
+ if (null != supportedSigAlgs && supportedSigAlgs.Count > 0)
+ {
+ this.m_supportedSignatureAlgorithms = supportedSigAlgs;
+
+ TlsExtensionsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, supportedSigAlgs);
+ }
+
+ IList supportedSigAlgsCert = GetSupportedSignatureAlgorithmsCert();
+ if (null != supportedSigAlgsCert && supportedSigAlgsCert.Count > 0)
+ {
+ this.m_supportedSignatureAlgorithmsCert = supportedSigAlgsCert;
+
+ TlsExtensionsUtilities.AddSignatureAlgorithmsCertExtension(clientExtensions, supportedSigAlgsCert);
+ }
+ }
+
+ IList namedGroupRoles = GetNamedGroupRoles();
+
+ IList supportedGroups = GetSupportedGroups(namedGroupRoles);
+ if (supportedGroups != null && supportedGroups.Count > 0)
+ {
+ this.m_supportedGroups = supportedGroups;
+
+ TlsExtensionsUtilities.AddSupportedGroupsExtension(clientExtensions, supportedGroups);
+ }
+
+ if (offeringPreTlsV13)
+ {
+ if (namedGroupRoles.Contains(NamedGroupRole.ecdh) ||
+ namedGroupRoles.Contains(NamedGroupRole.ecdsa))
+ {
+ TlsExtensionsUtilities.AddSupportedPointFormatsExtension(clientExtensions,
+ new short[]{ ECPointFormat.uncompressed });
+ }
+ }
+
+ return clientExtensions;
+ }
+
+ public virtual IList GetEarlyKeyShareGroups()
+ {
+ /*
+ * RFC 8446 4.2.8. Each KeyShareEntry value MUST correspond to a group offered in the
+ * "supported_groups" extension and MUST appear in the same order. However, the values MAY
+ * be a non-contiguous subset of the "supported_groups" extension and MAY omit the most
+ * preferred groups.
+ */
+
+ if (null == m_supportedGroups || m_supportedGroups.Count < 1)
+ return null;
+
+ if (m_supportedGroups.Contains(NamedGroup.x25519))
+ return TlsUtilities.VectorOfOne(NamedGroup.x25519);
+
+ if (m_supportedGroups.Contains(NamedGroup.secp256r1))
+ return TlsUtilities.VectorOfOne(NamedGroup.secp256r1);
+
+ return TlsUtilities.VectorOfOne(m_supportedGroups[0]);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ }
+
+ public virtual void NotifySessionToResume(TlsSession session)
+ {
+ }
+
+ public virtual void NotifySessionID(byte[] sessionID)
+ {
+ }
+
+ public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ if (null == serverExtensions)
+ return;
+
+ SecurityParameters securityParameters = m_context.SecurityParameters;
+ bool isTlsV13 = TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion);
+
+ if (isTlsV13)
+ {
+ /*
+ * NOTE: From TLS 1.3 the protocol classes are strict about what extensions can appear.
+ */
+ }
+ else
+ {
+ /*
+ * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension.
+ */
+ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.signature_algorithms);
+ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.signature_algorithms_cert);
+
+ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.supported_groups);
+
+ int selectedCipherSuite = securityParameters.CipherSuite;
+
+ if (TlsEccUtilities.IsEccCipherSuite(selectedCipherSuite))
+ {
+ // We only support uncompressed format, this is just to validate the extension, if present.
+ TlsExtensionsUtilities.GetSupportedPointFormatsExtension(serverExtensions);
+ }
+ else
+ {
+ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.ec_point_formats);
+ }
+
+ /*
+ * RFC 7685 3. The server MUST NOT echo the extension.
+ */
+ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.padding);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void ProcessServerSupplementalData(IList serverSupplementalData)
+ {
+ if (serverSupplementalData != null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public abstract TlsAuthentication GetAuthentication();
+
+ /// <exception cref="IOException"/>
+ public virtual IList GetClientSupplementalData()
+ {
+ return null;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket)
+ {
+ }
+ }
+}
diff --git a/crypto/src/tls/AbstractTlsContext.cs b/crypto/src/tls/AbstractTlsContext.cs
new file mode 100644
index 000000000..e8071bfab
--- /dev/null
+++ b/crypto/src/tls/AbstractTlsContext.cs
@@ -0,0 +1,282 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.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 static TlsNonceGenerator CreateNonceGenerator(TlsCrypto crypto, int connectionEnd)
+ {
+ byte[] additionalSeedMaterial = new byte[16];
+ Pack.UInt64_To_BE((ulong)NextCounterValue(), additionalSeedMaterial, 0);
+ Pack.UInt64_To_BE((ulong)Times.NanoTime(), additionalSeedMaterial, 8);
+ additionalSeedMaterial[0] &= 0x7F;
+ additionalSeedMaterial[0] |= (byte)(connectionEnd << 7);
+
+ return crypto.CreateNonceGenerator(additionalSeedMaterial);
+ }
+
+ private readonly TlsCrypto m_crypto;
+ private readonly int m_connectionEnd;
+ private readonly TlsNonceGenerator m_nonceGenerator;
+
+ private SecurityParameters m_securityParameters = null;
+ private ProtocolVersion[] m_clientSupportedVersions = null;
+ private ProtocolVersion m_clientVersion = null;
+ private ProtocolVersion m_rsaPreMasterSecretVersion = null;
+ private TlsSession m_session = null;
+ private object m_userObject = null;
+ private bool m_connected = false;
+
+ internal AbstractTlsContext(TlsCrypto crypto, int connectionEnd)
+ {
+ this.m_crypto = crypto;
+ this.m_connectionEnd = connectionEnd;
+ this.m_nonceGenerator = CreateNonceGenerator(crypto, connectionEnd);
+ }
+
+ /// <exception cref="IOException"/>
+ internal void HandshakeBeginning(TlsPeer peer)
+ {
+ lock (this)
+ {
+ if (null != m_securityParameters)
+ throw new TlsFatalAlert(AlertDescription.internal_error, "Handshake already started");
+
+ m_securityParameters = new SecurityParameters();
+ m_securityParameters.m_entity = m_connectionEnd;
+ }
+
+ peer.NotifyHandshakeBeginning();
+ }
+
+ /// <exception cref="IOException"/>
+ internal void HandshakeComplete(TlsPeer peer, TlsSession session)
+ {
+ lock (this)
+ {
+ if (null == m_securityParameters)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_session = session;
+ this.m_connected = true;
+ }
+
+ peer.NotifyHandshakeComplete();
+ }
+
+ internal bool IsConnected
+ {
+ get { lock (this) return m_connected; }
+ }
+
+ public TlsCrypto Crypto
+ {
+ get { return m_crypto; }
+ }
+
+ public virtual TlsNonceGenerator NonceGenerator
+ {
+ get { return m_nonceGenerator; }
+ }
+
+ public SecurityParameters SecurityParameters
+ {
+ get { lock (this) return m_securityParameters; }
+ }
+
+ public abstract bool IsServer { get; }
+
+ public virtual ProtocolVersion[] ClientSupportedVersions
+ {
+ get { return m_clientSupportedVersions; }
+ }
+
+ internal void SetClientSupportedVersions(ProtocolVersion[] clientSupportedVersions)
+ {
+ this.m_clientSupportedVersions = clientSupportedVersions;
+ }
+
+ public virtual ProtocolVersion ClientVersion
+ {
+ get { return m_clientVersion; }
+ }
+
+ internal void SetClientVersion(ProtocolVersion clientVersion)
+ {
+ this.m_clientVersion = clientVersion;
+ }
+
+ public virtual ProtocolVersion RsaPreMasterSecretVersion
+ {
+ get { return m_rsaPreMasterSecretVersion; }
+ }
+
+ internal void SetRsaPreMasterSecretVersion(ProtocolVersion rsaPreMasterSecretVersion)
+ {
+ this.m_rsaPreMasterSecretVersion = rsaPreMasterSecretVersion;
+ }
+
+ public virtual ProtocolVersion ServerVersion
+ {
+ get { return SecurityParameters.NegotiatedVersion; }
+ }
+
+ public virtual TlsSession ResumableSession
+ {
+ get
+ {
+ TlsSession session = Session;
+ if (session == null || !session.IsResumable)
+ return null;
+
+ return session;
+ }
+ }
+
+ public virtual TlsSession Session
+ {
+ get { return m_session; }
+ }
+
+ public virtual object UserObject
+ {
+ get { return m_userObject; }
+ set { this.m_userObject = value; }
+ }
+
+ public virtual byte[] ExportChannelBinding(int channelBinding)
+ {
+ if (!IsConnected)
+ throw new InvalidOperationException("Export of channel bindings unavailable before handshake completion");
+
+ SecurityParameters securityParameters = SecurityParameters;
+
+ if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
+ return null;
+
+ switch (channelBinding)
+ {
+ case ChannelBinding.tls_server_end_point:
+ {
+ byte[] tlsServerEndPoint = securityParameters.TlsServerEndPoint;
+
+ return TlsUtilities.IsNullOrEmpty(tlsServerEndPoint) ? null : Arrays.Clone(tlsServerEndPoint);
+ }
+
+ case ChannelBinding.tls_unique:
+ {
+ return Arrays.Clone(securityParameters.TlsUnique);
+ }
+
+ case ChannelBinding.tls_unique_for_telnet:
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ public virtual byte[] ExportEarlyKeyingMaterial(string asciiLabel, byte[] context, int length)
+ {
+ // TODO[tls13] Ensure early_exporter_master_secret is available suitably early!
+ if (!IsConnected)
+ throw new InvalidOperationException("Export of early key material only available during handshake");
+
+ SecurityParameters sp = SecurityParameters;
+
+ return ExportKeyingMaterial13(CheckEarlyExportSecret(sp.EarlyExporterMasterSecret),
+ sp.PrfHashAlgorithm, asciiLabel, context, length);
+ }
+
+ public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context, int length)
+ {
+ if (!IsConnected)
+ throw new InvalidOperationException("Export of key material unavailable before handshake completion");
+
+ /*
+ * TODO[tls13] Introduce a TlsExporter interface? Avoid calculating (early) exporter
+ * secret(s) unless the peer actually uses it.
+ */
+ SecurityParameters sp = SecurityParameters;
+
+ if (!sp.IsExtendedMasterSecret)
+ {
+ /*
+ * RFC 7627 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] [..].
+ */
+ throw new InvalidOperationException("Export of key material requires extended_master_secret");
+ }
+
+ if (TlsUtilities.IsTlsV13(sp.NegotiatedVersion))
+ {
+ return ExportKeyingMaterial13(CheckExportSecret(sp.ExporterMasterSecret), sp.PrfHashAlgorithm,
+ asciiLabel, context, length);
+ }
+
+ byte[] seed = TlsUtilities.CalculateExporterSeed(sp, context);
+
+ return TlsUtilities.Prf(sp, CheckExportSecret(sp.MasterSecret), asciiLabel, seed, length).Extract();
+ }
+
+ protected virtual byte[] ExportKeyingMaterial13(TlsSecret secret, short hashAlgorithm, string asciiLabel,
+ byte[] context, int length)
+ {
+ if (null == context)
+ {
+ context = TlsUtilities.EmptyBytes;
+ }
+ else if (!TlsUtilities.IsValidUint16(context.Length))
+ {
+ throw new ArgumentException("must have length less than 2^16 (or be null)", "context");
+ }
+
+ return TlsCryptoUtilities.HkdfExpandLabel(secret, hashAlgorithm, asciiLabel, context, length).Extract();
+ }
+
+ protected virtual TlsSecret CheckEarlyExportSecret(TlsSecret secret)
+ {
+ if (null == secret)
+ {
+ // TODO[tls13] For symmetry with normal export, ideally available for NotifyHandshakeBeginning() only
+ //throw new InvalidOperationException("Export of early key material only available from NotifyHandshakeBeginning()");
+ throw new InvalidOperationException("Export of early key material not available for this handshake");
+ }
+ return secret;
+ }
+
+ protected virtual TlsSecret CheckExportSecret(TlsSecret secret)
+ {
+ if (null == secret)
+ throw new InvalidOperationException(
+ "Export of key material only available from NotifyHandshakeComplete()");
+
+ return secret;
+ }
+ }
+}
diff --git a/crypto/src/tls/AbstractTlsKeyExchange.cs b/crypto/src/tls/AbstractTlsKeyExchange.cs
new file mode 100644
index 000000000..4e61f4c5a
--- /dev/null
+++ b/crypto/src/tls/AbstractTlsKeyExchange.cs
@@ -0,0 +1,90 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base class for supporting a TLS key exchange implementation.</summary>
+ public abstract class AbstractTlsKeyExchange
+ : TlsKeyExchange
+ {
+ protected readonly int m_keyExchange;
+
+ protected TlsContext m_context;
+
+ protected AbstractTlsKeyExchange(int keyExchange)
+ {
+ this.m_keyExchange = keyExchange;
+ }
+
+ public virtual void Init(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public abstract void SkipServerCredentials();
+
+ public abstract void ProcessServerCredentials(TlsCredentials serverCredentials);
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ 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 virtual short[] GetClientCertificateTypes()
+ {
+ return null;
+ }
+
+ 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 virtual bool RequiresCertificateVerify
+ {
+ get { return true; }
+ }
+
+ public abstract TlsSecret GeneratePreMasterSecret();
+ }
+}
diff --git a/crypto/src/tls/AbstractTlsKeyExchangeFactory.cs b/crypto/src/tls/AbstractTlsKeyExchangeFactory.cs
new file mode 100644
index 000000000..946ad5f0a
--- /dev/null
+++ b/crypto/src/tls/AbstractTlsKeyExchangeFactory.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base class for supporting a TLS key exchange factory implementation.</summary>
+ public abstract class AbstractTlsKeyExchangeFactory
+ : TlsKeyExchangeFactory
+ {
+ public virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateDHanonKeyExchangeClient(int keyExchange, TlsDHGroupVerifier dhGroupVerifier)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateDHanonKeyExchangeServer(int keyExchange, TlsDHConfig dhConfig)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateDheKeyExchangeClient(int keyExchange, TlsDHGroupVerifier dhGroupVerifier)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateDheKeyExchangeServer(int keyExchange, TlsDHConfig dhConfig)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateECDHanonKeyExchangeClient(int keyExchange)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateECDHanonKeyExchangeServer(int keyExchange, TlsECConfig ecConfig)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateECDheKeyExchangeClient(int keyExchange)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateECDheKeyExchangeServer(int keyExchange, TlsECConfig ecConfig)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreatePskKeyExchangeClient(int keyExchange, TlsPskIdentity pskIdentity,
+ TlsDHGroupVerifier dhGroupVerifier)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreatePskKeyExchangeServer(int keyExchange,
+ TlsPskIdentityManager pskIdentityManager, TlsDHConfig dhConfig, TlsECConfig ecConfig)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateRsaKeyExchange(int keyExchange)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateSrpKeyExchangeClient(int keyExchange, TlsSrpIdentity srpIdentity,
+ TlsSrpConfigVerifier srpConfigVerifier)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual TlsKeyExchange CreateSrpKeyExchangeServer(int keyExchange,
+ TlsSrpLoginParameters loginParameters)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/crypto/src/tls/AbstractTlsPeer.cs b/crypto/src/tls/AbstractTlsPeer.cs
new file mode 100644
index 000000000..ad9d83e76
--- /dev/null
+++ b/crypto/src/tls/AbstractTlsPeer.cs
@@ -0,0 +1,157 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base class for a TLS client or server.</summary>
+ public abstract class AbstractTlsPeer
+ : TlsPeer
+ {
+ private readonly TlsCrypto m_crypto;
+
+ private volatile TlsCloseable m_closeHandle;
+
+ protected AbstractTlsPeer(TlsCrypto crypto)
+ {
+ this.m_crypto = crypto;
+ }
+
+ /// <summary>Get the <see cref="ProtocolVersion"/> values that are supported by this peer.</summary>
+ /// <remarks>
+ /// WARNING: Mixing DTLS and TLS versions in the returned array is currently NOT supported. Use a separate
+ /// (sub-)class for each case.
+ /// </remarks>
+ /// <returns>an array of supported <see cref="ProtocolVersion"/> values.</returns>
+ protected virtual ProtocolVersion[] GetSupportedVersions()
+ {
+ // TODO[tls13] Enable TLSv13 by default in due course
+ return ProtocolVersion.TLSv12.DownTo(ProtocolVersion.TLSv10);
+ }
+
+ protected abstract int[] GetSupportedCipherSuites();
+
+ /// <exception cref="IOException"/>
+ public virtual void Cancel()
+ {
+ TlsCloseable closeHandle = this.m_closeHandle;
+ if (null != closeHandle)
+ {
+ closeHandle.Close();
+ }
+ }
+
+ public virtual TlsCrypto Crypto
+ {
+ get { return m_crypto; }
+ }
+
+ public virtual void NotifyCloseHandle(TlsCloseable closeHandle)
+ {
+ this.m_closeHandle = closeHandle;
+ }
+
+ public abstract ProtocolVersion[] GetProtocolVersions();
+
+ public abstract int[] GetCipherSuites();
+
+ /// <exception cref="IOException"/>
+ public virtual void NotifyHandshakeBeginning()
+ {
+ }
+
+ public virtual int GetHandshakeTimeoutMillis()
+ {
+ return 0;
+ }
+
+ public virtual bool AllowLegacyResumption()
+ {
+ return false;
+ }
+
+ public virtual int GetMaxCertificateChainLength()
+ {
+ return 10;
+ }
+
+ public virtual int GetMaxHandshakeMessageSize()
+ {
+ return 32768;
+ }
+
+ public virtual bool RequiresCloseNotify()
+ {
+ return true;
+ }
+
+ public virtual bool RequiresExtendedMasterSecret()
+ {
+ return false;
+ }
+
+ public virtual bool ShouldCheckSigAlgOfPeerCerts()
+ {
+ return true;
+ }
+
+ public virtual bool ShouldUseExtendedMasterSecret()
+ {
+ return true;
+ }
+
+ public virtual bool ShouldUseExtendedPadding()
+ {
+ return false;
+ }
+
+ 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;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ {
+ if (!secureRenegotiation)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsKeyExchangeFactory GetKeyExchangeFactory()
+ {
+ return new DefaultTlsKeyExchangeFactory();
+ }
+
+ public virtual void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ }
+
+ public virtual void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void NotifyHandshakeComplete()
+ {
+ }
+
+ public virtual TlsHeartbeat GetHeartbeat()
+ {
+ return null;
+ }
+
+ public virtual short GetHeartbeatPolicy()
+ {
+ return HeartbeatMode.peer_not_allowed_to_send;
+ }
+ }
+}
diff --git a/crypto/src/tls/AbstractTlsServer.cs b/crypto/src/tls/AbstractTlsServer.cs
new file mode 100644
index 000000000..7514e3618
--- /dev/null
+++ b/crypto/src/tls/AbstractTlsServer.cs
@@ -0,0 +1,578 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base class for a TLS server.</summary>
+ public abstract class AbstractTlsServer
+ : AbstractTlsPeer, TlsServer
+ {
+ protected TlsServerContext m_context;
+ protected ProtocolVersion[] m_protocolVersions;
+ protected int[] m_cipherSuites;
+
+ protected int[] m_offeredCipherSuites;
+ protected IDictionary m_clientExtensions;
+
+ protected bool m_encryptThenMACOffered;
+ protected short m_maxFragmentLengthOffered;
+ protected bool m_truncatedHMacOffered;
+ protected bool m_clientSentECPointFormats;
+ protected CertificateStatusRequest m_certificateStatusRequest;
+ protected IList m_statusRequestV2;
+ protected IList m_trustedCAKeys;
+
+ protected int m_selectedCipherSuite;
+ protected IList m_clientProtocolNames;
+ protected ProtocolName m_selectedProtocolName;
+
+ protected readonly IDictionary m_serverExtensions = Platform.CreateHashtable();
+
+ public AbstractTlsServer(TlsCrypto crypto)
+ : base(crypto)
+ {
+ }
+
+ protected virtual bool AllowCertificateStatus()
+ {
+ return true;
+ }
+
+ protected virtual bool AllowEncryptThenMac()
+ {
+ return true;
+ }
+
+ protected virtual bool AllowMultiCertStatus()
+ {
+ return false;
+ }
+
+ protected virtual bool AllowTruncatedHmac()
+ {
+ return false;
+ }
+
+ protected virtual bool AllowTrustedCAIndication()
+ {
+ return false;
+ }
+
+ protected virtual int GetMaximumNegotiableCurveBits()
+ {
+ int[] clientSupportedGroups = m_context.SecurityParameters.ClientSupportedGroups;
+ if (clientSupportedGroups == 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 NamedGroup.GetMaximumCurveBits();
+ }
+
+ int maxBits = 0;
+ for (int i = 0; i < clientSupportedGroups.Length; ++i)
+ {
+ maxBits = System.Math.Max(maxBits, NamedGroup.GetCurveBits(clientSupportedGroups[i]));
+ }
+ return maxBits;
+ }
+
+ protected virtual int GetMaximumNegotiableFiniteFieldBits()
+ {
+ int[] clientSupportedGroups = m_context.SecurityParameters.ClientSupportedGroups;
+ if (clientSupportedGroups == null)
+ {
+ return NamedGroup.GetMaximumFiniteFieldBits();
+ }
+
+ int maxBits = 0;
+ for (int i = 0; i < clientSupportedGroups.Length; ++i)
+ {
+ maxBits = System.Math.Max(maxBits, NamedGroup.GetFiniteFieldBits(clientSupportedGroups[i]));
+ }
+ return maxBits;
+ }
+
+ protected virtual IList GetProtocolNames()
+ {
+ return null;
+ }
+
+ protected virtual bool IsSelectableCipherSuite(int cipherSuite, int availCurveBits, int availFiniteFieldBits,
+ IList sigAlgs)
+ {
+ // TODO[tls13] The version check should be separated out (eventually select ciphersuite before version)
+ return TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, m_context.ServerVersion)
+ && availCurveBits >= TlsEccUtilities.GetMinimumCurveBits(cipherSuite)
+ && availFiniteFieldBits >= TlsDHUtilities.GetMinimumFiniteFieldBits(cipherSuite)
+ && TlsUtilities.IsValidCipherSuiteForSignatureAlgorithms(cipherSuite, sigAlgs);
+ }
+
+ protected virtual bool PreferLocalCipherSuites()
+ {
+ return false;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual bool SelectCipherSuite(int cipherSuite)
+ {
+ this.m_selectedCipherSuite = cipherSuite;
+ return true;
+ }
+
+ protected virtual int SelectDH(int minimumFiniteFieldBits)
+ {
+ int[] clientSupportedGroups = m_context.SecurityParameters.ClientSupportedGroups;
+ if (clientSupportedGroups == null)
+ return SelectDHDefault(minimumFiniteFieldBits);
+
+ // Try to find a supported named group of the required size from the client's list.
+ for (int i = 0; i < clientSupportedGroups.Length; ++i)
+ {
+ int namedGroup = clientSupportedGroups[i];
+ if (NamedGroup.GetFiniteFieldBits(namedGroup) >= minimumFiniteFieldBits)
+ return namedGroup;
+ }
+
+ return -1;
+ }
+
+ protected virtual int SelectDHDefault(int minimumFiniteFieldBits)
+ {
+ return minimumFiniteFieldBits <= 2048 ? NamedGroup.ffdhe2048
+ : minimumFiniteFieldBits <= 3072 ? NamedGroup.ffdhe3072
+ : minimumFiniteFieldBits <= 4096 ? NamedGroup.ffdhe4096
+ : minimumFiniteFieldBits <= 6144 ? NamedGroup.ffdhe6144
+ : minimumFiniteFieldBits <= 8192 ? NamedGroup.ffdhe8192
+ : -1;
+ }
+
+ protected virtual int SelectECDH(int minimumCurveBits)
+ {
+ int[] clientSupportedGroups = m_context.SecurityParameters.ClientSupportedGroups;
+ if (clientSupportedGroups == null)
+ return SelectECDHDefault(minimumCurveBits);
+
+ // Try to find a supported named group of the required size from the client's list.
+ for (int i = 0; i < clientSupportedGroups.Length; ++i)
+ {
+ int namedGroup = clientSupportedGroups[i];
+ if (NamedGroup.GetCurveBits(namedGroup) >= minimumCurveBits)
+ return namedGroup;
+ }
+
+ return -1;
+ }
+
+ protected virtual int SelectECDHDefault(int minimumCurveBits)
+ {
+ return minimumCurveBits <= 256 ? NamedGroup.secp256r1
+ : minimumCurveBits <= 384 ? NamedGroup.secp384r1
+ : minimumCurveBits <= 521 ? NamedGroup.secp521r1
+ : -1;
+ }
+
+ protected virtual ProtocolName SelectProtocolName()
+ {
+ IList serverProtocolNames = GetProtocolNames();
+ if (null == serverProtocolNames || serverProtocolNames.Count < 1)
+ return null;
+
+ ProtocolName result = SelectProtocolName(m_clientProtocolNames, serverProtocolNames);
+ if (null == result)
+ throw new TlsFatalAlert(AlertDescription.no_application_protocol);
+
+ return result;
+ }
+
+ protected virtual ProtocolName SelectProtocolName(IList clientProtocolNames, IList serverProtocolNames)
+ {
+ foreach (ProtocolName serverProtocolName in serverProtocolNames)
+ {
+ if (clientProtocolNames.Contains(serverProtocolName))
+ return serverProtocolName;
+ }
+ return null;
+ }
+
+ protected virtual bool ShouldSelectProtocolNameEarly()
+ {
+ return true;
+ }
+
+ public virtual void Init(TlsServerContext context)
+ {
+ this.m_context = context;
+
+ this.m_protocolVersions = GetSupportedVersions();
+ this.m_cipherSuites = GetSupportedCipherSuites();
+ }
+
+ public override ProtocolVersion[] GetProtocolVersions()
+ {
+ return m_protocolVersions;
+ }
+
+ public override int[] GetCipherSuites()
+ {
+ return m_cipherSuites;
+ }
+
+ public override void NotifyHandshakeBeginning()
+ {
+ base.NotifyHandshakeBeginning();
+
+ this.m_offeredCipherSuites = null;
+ this.m_clientExtensions = null;
+ this.m_encryptThenMACOffered = false;
+ this.m_maxFragmentLengthOffered = 0;
+ this.m_truncatedHMacOffered = false;
+ this.m_clientSentECPointFormats = false;
+ this.m_certificateStatusRequest = null;
+ this.m_selectedCipherSuite = -1;
+ this.m_selectedProtocolName = null;
+ this.m_serverExtensions.Clear();
+ }
+
+ public virtual TlsSession GetSessionToResume(byte[] sessionID)
+ {
+ return null;
+ }
+
+ public virtual byte[] GetNewSessionID()
+ {
+ return null;
+ }
+
+ public virtual void NotifySession(TlsSession session)
+ {
+ }
+
+ public virtual void NotifyClientVersion(ProtocolVersion clientVersion)
+ {
+ }
+
+ public virtual void NotifyFallback(bool isFallback)
+ {
+ /*
+ * RFC 7507 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 a fatal inappropriate_fallback
+ * alert [..].
+ */
+ if (isFallback)
+ {
+ ProtocolVersion[] serverVersions = GetProtocolVersions();
+ ProtocolVersion clientVersion = m_context.ClientVersion;
+
+ ProtocolVersion latestServerVersion;
+ if (clientVersion.IsTls)
+ {
+ latestServerVersion = ProtocolVersion.GetLatestTls(serverVersions);
+ }
+ else if (clientVersion.IsDtls)
+ {
+ latestServerVersion = ProtocolVersion.GetLatestDtls(serverVersions);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (null != latestServerVersion && latestServerVersion.IsLaterVersionOf(clientVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.inappropriate_fallback);
+ }
+ }
+ }
+
+ public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites)
+ {
+ this.m_offeredCipherSuites = offeredCipherSuites;
+ }
+
+ public virtual void ProcessClientExtensions(IDictionary clientExtensions)
+ {
+ this.m_clientExtensions = clientExtensions;
+
+ if (null != clientExtensions)
+ {
+ this.m_clientProtocolNames = TlsExtensionsUtilities.GetAlpnExtensionClient(clientExtensions);
+
+ if (ShouldSelectProtocolNameEarly())
+ {
+ if (null != m_clientProtocolNames && m_clientProtocolNames.Count > 0)
+ {
+ this.m_selectedProtocolName = SelectProtocolName();
+ }
+ }
+
+ // TODO[tls13] Don't need these if we have negotiated (D)TLS 1.3+
+ {
+ this.m_encryptThenMACOffered = TlsExtensionsUtilities.HasEncryptThenMacExtension(clientExtensions);
+ this.m_truncatedHMacOffered = TlsExtensionsUtilities.HasTruncatedHmacExtension(clientExtensions);
+ this.m_statusRequestV2 = TlsExtensionsUtilities.GetStatusRequestV2Extension(clientExtensions);
+ this.m_trustedCAKeys = TlsExtensionsUtilities.GetTrustedCAKeysExtensionClient(clientExtensions);
+
+ // We only support uncompressed format, this is just to validate the extension, and note its presence.
+ this.m_clientSentECPointFormats =
+ null != TlsExtensionsUtilities.GetSupportedPointFormatsExtension(clientExtensions);
+ }
+
+ this.m_certificateStatusRequest = TlsExtensionsUtilities.GetStatusRequestExtension(clientExtensions);
+
+ this.m_maxFragmentLengthOffered = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions);
+ if (m_maxFragmentLengthOffered >= 0 && !MaxFragmentLength.IsValid(m_maxFragmentLengthOffered))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public virtual ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion[] serverVersions = GetProtocolVersions();
+ ProtocolVersion[] clientVersions = m_context.ClientSupportedVersions;
+
+ foreach (ProtocolVersion clientVersion in clientVersions)
+ {
+ if (ProtocolVersion.Contains(serverVersions, clientVersion))
+ return clientVersion;
+ }
+
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+
+ public virtual int[] GetSupportedGroups()
+ {
+ // TODO[tls13] The rest of this class assumes all named groups are supported
+ return new int[]{ NamedGroup.x25519, NamedGroup.x448, NamedGroup.secp256r1, NamedGroup.secp384r1,
+ NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096 };
+ }
+
+ public virtual int GetSelectedCipherSuite()
+ {
+ SecurityParameters securityParameters = m_context.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (TlsUtilities.IsTlsV13(negotiatedVersion))
+ {
+ int commonCipherSuite13 = TlsUtilities.GetCommonCipherSuite13(negotiatedVersion, m_offeredCipherSuites,
+ GetCipherSuites(), PreferLocalCipherSuites());
+
+ if (commonCipherSuite13 >= 0 && SelectCipherSuite(commonCipherSuite13))
+ {
+ return commonCipherSuite13;
+ }
+ }
+ else
+ {
+ /*
+ * 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.
+ */
+ IList sigAlgs = TlsUtilities.GetUsableSignatureAlgorithms(securityParameters.ClientSigAlgs);
+
+ /*
+ * 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 [...].
+ */
+ int availCurveBits = GetMaximumNegotiableCurveBits();
+ int availFiniteFieldBits = GetMaximumNegotiableFiniteFieldBits();
+
+ int[] cipherSuites = TlsUtilities.GetCommonCipherSuites(m_offeredCipherSuites, GetCipherSuites(),
+ PreferLocalCipherSuites());
+
+ for (int i = 0; i < cipherSuites.Length; ++i)
+ {
+ int cipherSuite = cipherSuites[i];
+ if (IsSelectableCipherSuite(cipherSuite, availCurveBits, availFiniteFieldBits, sigAlgs)
+ && SelectCipherSuite(cipherSuite))
+ {
+ return cipherSuite;
+ }
+ }
+ }
+
+ throw new TlsFatalAlert(AlertDescription.handshake_failure, "No selectable cipher suite");
+ }
+
+ // IDictionary is (Int32 -> byte[])
+ public virtual IDictionary GetServerExtensions()
+ {
+ bool isTlsV13 = TlsUtilities.IsTlsV13(m_context);
+
+ if (isTlsV13)
+ {
+ if (null != m_certificateStatusRequest && AllowCertificateStatus())
+ {
+ /*
+ * TODO[tls13] RFC 8446 4.4.2.1. OCSP Status and SCT Extensions.
+ *
+ * OCSP information is carried in an extension for a CertificateEntry.
+ */
+ }
+ }
+ else
+ {
+ if (m_encryptThenMACOffered && AllowEncryptThenMac())
+ {
+ /*
+ * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
+ * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
+ * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
+ * client.
+ */
+ if (TlsUtilities.IsBlockCipherSuite(m_selectedCipherSuite))
+ {
+ TlsExtensionsUtilities.AddEncryptThenMacExtension(m_serverExtensions);
+ }
+ }
+
+ if (m_truncatedHMacOffered && AllowTruncatedHmac())
+ {
+ TlsExtensionsUtilities.AddTruncatedHmacExtension(m_serverExtensions);
+ }
+
+ if (m_clientSentECPointFormats && TlsEccUtilities.IsEccCipherSuite(m_selectedCipherSuite))
+ {
+ /*
+ * 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.
+ */
+ TlsExtensionsUtilities.AddSupportedPointFormatsExtension(m_serverExtensions,
+ new short[]{ ECPointFormat.uncompressed });
+ }
+
+ // TODO[tls13] See RFC 8446 4.4.2.1
+ if (null != m_statusRequestV2 && AllowMultiCertStatus())
+ {
+ /*
+ * RFC 6961 2.2. If a server returns a "CertificateStatus" message in response to a
+ * "status_request_v2" request, then the server MUST have included an extension of type
+ * "status_request_v2" with empty "extension_data" in the extended server hello..
+ */
+ TlsExtensionsUtilities.AddEmptyExtensionData(m_serverExtensions, ExtensionType.status_request_v2);
+ }
+ else if (null != this.m_certificateStatusRequest && AllowCertificateStatus())
+ {
+ /*
+ * RFC 6066 8. 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.
+ */
+ TlsExtensionsUtilities.AddEmptyExtensionData(m_serverExtensions, ExtensionType.status_request);
+ }
+
+ if (null != m_trustedCAKeys && AllowTrustedCAIndication())
+ {
+ TlsExtensionsUtilities.AddTrustedCAKeysExtensionServer(m_serverExtensions);
+ }
+ }
+
+ if (m_maxFragmentLengthOffered >= 0 && MaxFragmentLength.IsValid(m_maxFragmentLengthOffered))
+ {
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(m_serverExtensions, m_maxFragmentLengthOffered);
+ }
+
+ return m_serverExtensions;
+ }
+
+ public virtual void GetServerExtensionsForConnection(IDictionary serverExtensions)
+ {
+ if (!ShouldSelectProtocolNameEarly())
+ {
+ if (null != m_clientProtocolNames && m_clientProtocolNames.Count > 0)
+ {
+ this.m_selectedProtocolName = SelectProtocolName();
+ }
+ }
+
+ /*
+ * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
+ * contents of this extension are irrelevant, and only the values in the new handshake
+ * messages are considered.
+ */
+ if (null == m_selectedProtocolName)
+ {
+ serverExtensions.Remove(ExtensionType.application_layer_protocol_negotiation);
+ }
+ else
+ {
+ TlsExtensionsUtilities.AddAlpnExtensionServer(serverExtensions, m_selectedProtocolName);
+ }
+ }
+
+ public virtual IList GetServerSupplementalData()
+ {
+ return null;
+ }
+
+ public abstract TlsCredentials GetCredentials();
+
+ public virtual CertificateStatus GetCertificateStatus()
+ {
+ return null;
+ }
+
+ public virtual CertificateRequest GetCertificateRequest()
+ {
+ return null;
+ }
+
+ public virtual TlsPskIdentityManager GetPskIdentityManager()
+ {
+ return null;
+ }
+
+ public virtual TlsSrpLoginParameters GetSrpLoginParameters()
+ {
+ return null;
+ }
+
+ public virtual TlsDHConfig GetDHConfig()
+ {
+ int minimumFiniteFieldBits = TlsDHUtilities.GetMinimumFiniteFieldBits(m_selectedCipherSuite);
+ int namedGroup = SelectDH(minimumFiniteFieldBits);
+ return TlsDHUtilities.CreateNamedDHConfig(m_context, namedGroup);
+ }
+
+ public virtual TlsECConfig GetECDHConfig()
+ {
+ int minimumCurveBits = TlsEccUtilities.GetMinimumCurveBits(m_selectedCipherSuite);
+ int namedGroup = SelectECDH(minimumCurveBits);
+ return TlsEccUtilities.CreateNamedECConfig(m_context, namedGroup);
+ }
+
+ 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 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/tls/AlertDescription.cs b/crypto/src/tls/AlertDescription.cs
new file mode 100644
index 000000000..dc207bb54
--- /dev/null
+++ b/crypto/src/tls/AlertDescription.cs
@@ -0,0 +1,323 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5246 7.2.</summary>
+ public abstract class AlertDescription
+ {
+ /// <summary>This message notifies the recipient that the sender will not send any more messages on this
+ /// connection.</summary>
+ /// <remarks>
+ /// Note that as of TLS 1.1, failure to properly close a connection no longer requires that a session not be
+ /// resumed. This is a change from TLS 1.0 ("The session becomes unresumable if any connection is terminated
+ /// without proper close_notify messages with level equal to warning.") to conform with widespread
+ /// implementation practice.
+ /// </remarks>
+ public const short close_notify = 0;
+
+ /// <summary>An inappropriate message was received.</summary>
+ /// <remarks>
+ /// This alert is always fatal and should never be observed in communication between proper implementations.
+ /// </remarks>
+ public const short unexpected_message = 10;
+
+ /// <summary>This alert is returned if a record is received with an incorrect MAC.</summary>
+ /// <remarks>
+ /// This alert also MUST be returned if an alert is sent because a TLSCiphertext decrypted in an invalid way:
+ /// either it wasn't an even multiple of the block length, or its padding values, when checked, weren't
+ /// correct. This message is always fatal and should never be observed in communication between proper
+ /// implementations (except when messages were corrupted in the network).
+ /// </remarks>
+ public const short bad_record_mac = 20;
+
+ /// <remarks>
+ /// This alert was used in some earlier versions of TLS, and may have permitted certain attacks against the CBC
+ /// mode [CBCATT]. It MUST NOT be sent by compliant implementations.
+ /// </remarks>
+ public const short decryption_failed = 21;
+
+ /// <summary>A TLSCiphertext record was received that had a length more than 2^14+2048 bytes, or a record
+ /// decrypted to a TLSCompressed record with more than 2^14+1024 bytes.</summary>
+ /// <remarks>
+ /// This message is always fatal and should never be observed in communication between proper implementations
+ /// (except when messages were corrupted in the network).
+ /// </remarks>
+ public const short record_overflow = 22;
+
+ /// <summary>The decompression function received improper input (e.g., data that would expand to excessive
+ /// length).</summary>
+ /// <remarks>
+ /// This message is always fatal and should never be observed in communication between proper implementations.
+ /// </remarks>
+ public const short decompression_failure = 30;
+
+ /// <summary>Reception of a handshake_failure alert message indicates that the sender was unable to negotiate
+ /// an acceptable set of security parameters given the options available.</summary>
+ /// <remarks>
+ /// This is a fatal error.
+ /// </remarks>
+ public const short handshake_failure = 40;
+
+ /// <remarks>
+ /// This alert was used in SSLv3 but not any version of TLS. It MUST NOT be sent by compliant implementations.
+ /// </remarks>
+ public const short no_certificate = 41;
+
+ /// <summary>A certificate was corrupt, contained signatures that did not verify correctly, etc.</summary>
+ public const short bad_certificate = 42;
+
+ /// <summary>A certificate was of an unsupported type.</summary>
+ public const short unsupported_certificate = 43;
+
+ /// <summary>A certificate was revoked by its signer.</summary>
+ public const short certificate_revoked = 44;
+
+ /// <summary>A certificate has expired or is not currently valid.</summary>
+ public const short certificate_expired = 45;
+
+ /// <summary>Some other (unspecified) issue arose in processing the certificate, rendering it unacceptable.
+ /// </summary>
+ public const short certificate_unknown = 46;
+
+ /// <summary>A field in the handshake was out of range or inconsistent with other fields.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short illegal_parameter = 47;
+
+ /// <summary>A valid certificate chain or partial chain was received, but the certificate was not accepted
+ /// because the CA certificate could not be located or couldn't be matched with a known, trusted CA.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short unknown_ca = 48;
+
+ /// <summary>A valid certificate was received, but when access control was applied, the sender decided not to
+ /// proceed with negotiation.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short access_denied = 49;
+
+ /// <summary>A message could not be decoded because some field was out of the specified range or the length of
+ /// the message was incorrect.</summary>
+ /// <remarks>
+ /// This message is always fatal and should never be observed in communication between proper
+ /// implementations (except when messages were corrupted in the network).
+ /// </remarks>
+ public const short decode_error = 50;
+
+ /// <summary>A handshake cryptographic operation failed, including being unable to correctly verify a signature
+ /// or validate a Finished message.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short decrypt_error = 51;
+
+ /// <remarks>
+ /// This alert was used in some earlier versions of TLS. It MUST NOT be sent by compliant implementations.
+ /// </remarks>
+ public const short export_restriction = 60;
+
+ /// <summary>The protocol version the client has attempted to negotiate is recognized but not supported.
+ /// </summary>
+ /// <remarks>
+ /// (For example, old protocol versions might be avoided for security reasons.) This message is always fatal.
+ /// </remarks>
+ public const short protocol_version = 70;
+
+ /// <summary>Returned instead of handshake_failure when a negotiation has failed specifically because the
+ /// server requires ciphers more secure than those supported by the client.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short insufficient_security = 71;
+
+ /// <summary>An internal error unrelated to the peer or the correctness of the protocol (such as a memory
+ /// allocation failure) makes it impossible to continue.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short internal_error = 80;
+
+ /// <summary>This handshake is being canceled for some reason unrelated to a protocol failure.</summary>
+ /// <remarks>
+ /// If the user cancels an operation after the handshake is complete, just closing the connection by sending a
+ /// close_notify is more appropriate. This alert should be followed by a close_notify. This message is
+ /// generally a warning.
+ /// </remarks>
+ public const short user_canceled = 90;
+
+ /// <summary>Sent by the client in response to a hello request or by the server in response to a client hello
+ /// after initial handshaking.</summary>
+ /// <remarks>
+ /// Either of these would normally lead to renegotiation; when that is not appropriate, the recipient should
+ /// respond with this alert. At that point, the original requester can decide whether to proceed with the
+ /// connection. One case where this would be appropriate is where a server has spawned a process to satisfy a
+ /// request; the process might receive security parameters (key length, authentication, etc.) at startup, and
+ /// it might be difficult to communicate changes to these parameters after that point. This message is always a
+ /// warning.
+ /// </remarks>
+ public const short no_renegotiation = 100;
+
+ /// <summary>Sent by clients that receive an extended server hello containing an extension that they did not
+ /// put in the corresponding client hello.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short unsupported_extension = 110;
+
+ /*
+ * RFC 3546
+ */
+
+ /// <summary>This alert is sent by servers who are unable to retrieve a certificate chain from the URL supplied
+ /// by the client(see Section 3.3).</summary>
+ /// <remarks>
+ /// This message MAY be fatal - for example if client authentication is required by the server for the
+ /// handshake to continue and the server is unable to retrieve the certificate chain, it may send a fatal
+ /// alert.
+ /// </remarks>
+ public const short certificate_unobtainable = 111;
+
+ /// <summary>This alert is sent by servers that receive a server_name extension request, but do not recognize
+ /// the server name.</summary>
+ /// <remarks>
+ /// This message MAY be fatal.
+ /// </remarks>
+ public const short unrecognized_name = 112;
+
+ /// <summary>This alert is sent by clients that receive an invalid certificate status response (see Section 3.6
+ /// ).</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short bad_certificate_status_response = 113;
+
+ /// <summary>This alert is sent by servers when a certificate hash does not match a client provided
+ /// certificate_hash.</summary>
+ /// <remarks>
+ /// This message is always fatal.
+ /// </remarks>
+ public const short bad_certificate_hash_value = 114;
+
+ /*
+ * RFC 4279
+ */
+
+ /// <summary>If the server does not recognize the PSK identity, it MAY respond with an "unknown_psk_identity"
+ /// alert message.</summary>
+ public const short unknown_psk_identity = 115;
+
+ /*
+ * RFC 7301
+ */
+
+ /// <summary>In the event that the server supports no protocols that the client advertises, then the server
+ /// SHALL respond with a fatal "no_application_protocol" alert.</summary>
+ public const short no_application_protocol = 120;
+
+ /*
+ * RFC 7507
+ */
+
+ /// <summary>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 a fatal inappropriate_fallback alert[..].</summary>
+ public const short inappropriate_fallback = 86;
+
+ /*
+ * RFC 8446
+ */
+
+ /// <summary>Sent by endpoints that receive a handshake message not containing an extension that is mandatory
+ /// to send for the offered TLS version or other negotiated parameters.</summary>
+ public const short missing_extension = 109;
+
+ /// <summary>Sent by servers when a client certificate is desired but none was provided by the client.
+ /// </summary>
+ public const short certificate_required = 116;
+
+ public static string GetName(short alertDescription)
+ {
+ switch (alertDescription)
+ {
+ case close_notify:
+ return "close_notify";
+ case unexpected_message:
+ return "unexpected_message";
+ case bad_record_mac:
+ return "bad_record_mac";
+ case decryption_failed:
+ return "decryption_failed";
+ case record_overflow:
+ return "record_overflow";
+ case decompression_failure:
+ return "decompression_failure";
+ case handshake_failure:
+ return "handshake_failure";
+ case no_certificate:
+ return "no_certificate";
+ case bad_certificate:
+ return "bad_certificate";
+ case unsupported_certificate:
+ return "unsupported_certificate";
+ case certificate_revoked:
+ return "certificate_revoked";
+ case certificate_expired:
+ return "certificate_expired";
+ case certificate_unknown:
+ return "certificate_unknown";
+ case illegal_parameter:
+ return "illegal_parameter";
+ case unknown_ca:
+ return "unknown_ca";
+ case access_denied:
+ return "access_denied";
+ case decode_error:
+ return "decode_error";
+ case decrypt_error:
+ return "decrypt_error";
+ case export_restriction:
+ return "export_restriction";
+ case protocol_version:
+ return "protocol_version";
+ case insufficient_security:
+ return "insufficient_security";
+ case internal_error:
+ return "internal_error";
+ case user_canceled:
+ return "user_canceled";
+ case no_renegotiation:
+ return "no_renegotiation";
+ case unsupported_extension:
+ return "unsupported_extension";
+ case certificate_unobtainable:
+ return "certificate_unobtainable";
+ case unrecognized_name:
+ return "unrecognized_name";
+ case bad_certificate_status_response:
+ return "bad_certificate_status_response";
+ case bad_certificate_hash_value:
+ return "bad_certificate_hash_value";
+ case unknown_psk_identity:
+ return "unknown_psk_identity";
+ case no_application_protocol:
+ return "no_application_protocol";
+ case inappropriate_fallback:
+ return "inappropriate_fallback";
+ case missing_extension:
+ return "missing_extension";
+ case certificate_required:
+ return "certificate_required";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short alertDescription)
+ {
+ return GetName(alertDescription) + "(" + alertDescription + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/AlertLevel.cs b/crypto/src/tls/AlertLevel.cs
new file mode 100644
index 000000000..6e128e04d
--- /dev/null
+++ b/crypto/src/tls/AlertLevel.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5246 7.2</summary>
+ public abstract class AlertLevel
+ {
+ public const short warning = 1;
+ public const short fatal = 2;
+
+ public static string GetName(short alertDescription)
+ {
+ switch (alertDescription)
+ {
+ case warning:
+ return "warning";
+ case fatal:
+ return "fatal";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short alertDescription)
+ {
+ return GetName(alertDescription) + "(" + alertDescription + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/BasicTlsPskIdentity.cs b/crypto/src/tls/BasicTlsPskIdentity.cs
new file mode 100644
index 000000000..4ed385a6b
--- /dev/null
+++ b/crypto/src/tls/BasicTlsPskIdentity.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>A basic PSK Identity holder.</summary>
+ public class BasicTlsPskIdentity
+ : TlsPskIdentity
+ {
+ protected readonly byte[] m_identity;
+ protected readonly byte[] m_psk;
+
+ public BasicTlsPskIdentity(byte[] identity, byte[] psk)
+ {
+ this.m_identity = Arrays.Clone(identity);
+ this.m_psk = Arrays.Clone(psk);
+ }
+
+ public BasicTlsPskIdentity(string identity, byte[] psk)
+ {
+ this.m_identity = Strings.ToUtf8ByteArray(identity);
+ this.m_psk = Arrays.Clone(psk);
+ }
+
+ public virtual void SkipIdentityHint()
+ {
+ }
+
+ public virtual void NotifyIdentityHint(byte[] psk_identity_hint)
+ {
+ }
+
+ public virtual byte[] GetPskIdentity()
+ {
+ return m_identity;
+ }
+
+ public byte[] GetPsk()
+ {
+ return Arrays.Clone(m_psk);
+ }
+ }
+}
diff --git a/crypto/src/tls/BasicTlsSrpIdentity.cs b/crypto/src/tls/BasicTlsSrpIdentity.cs
new file mode 100644
index 000000000..9a527a705
--- /dev/null
+++ b/crypto/src/tls/BasicTlsSrpIdentity.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>A basic SRP Identity holder.</summary>
+ public class BasicTlsSrpIdentity
+ : TlsSrpIdentity
+ {
+ protected readonly byte[] m_identity;
+ protected readonly byte[] m_password;
+
+ public BasicTlsSrpIdentity(byte[] identity, byte[] password)
+ {
+ this.m_identity = Arrays.Clone(identity);
+ this.m_password = Arrays.Clone(password);
+ }
+
+ public BasicTlsSrpIdentity(string identity, string password)
+ {
+ this.m_identity = Strings.ToUtf8ByteArray(identity);
+ this.m_password = Strings.ToUtf8ByteArray(password);
+ }
+
+ public virtual byte[] GetSrpIdentity()
+ {
+ return m_identity;
+ }
+
+ public virtual byte[] GetSrpPassword()
+ {
+ return m_password;
+ }
+ }
+}
diff --git a/crypto/src/tls/ByteQueue.cs b/crypto/src/tls/ByteQueue.cs
new file mode 100644
index 000000000..b0241972e
--- /dev/null
+++ b/crypto/src/tls/ByteQueue.cs
@@ -0,0 +1,195 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>A queue for bytes. This file could be more optimized.</summary>
+ public sealed class ByteQueue
+ {
+ /// <returns>The smallest number which can be written as 2^x which is bigger than i.</returns>
+ public static int NextTwoPow(int i)
+ {
+ /*
+ * This code is based of a lot of code I found on the Internet which mostly
+ * referenced a book called "Hacking delight".
+ */
+ i |= i >> 1;
+ i |= i >> 2;
+ i |= i >> 4;
+ i |= i >> 8;
+ i |= i >> 16;
+ return i + 1;
+ }
+
+ /// <summary>The buffer where we store our data.</summary>
+ private byte[] m_databuf;
+
+ /// <summary>How many bytes at the beginning of the buffer are skipped.</summary>
+ private int m_skipped = 0;
+
+ /// <summary>How many bytes in the buffer are valid data.</summary>
+ private int m_available = 0;
+
+ private bool m_readOnlyBuf = false;
+
+ public ByteQueue()
+ : this(0)
+ {
+ }
+
+ public ByteQueue(int capacity)
+ {
+ this.m_databuf = capacity == 0 ? TlsUtilities.EmptyBytes : new byte[capacity];
+ }
+
+ public ByteQueue(byte[] buf, int off, int len)
+ {
+ this.m_databuf = buf;
+ this.m_skipped = off;
+ this.m_available = len;
+ this.m_readOnlyBuf = true;
+ }
+
+ /// <summary>Add some data to our buffer.</summary>
+ /// <param name="buf">A byte-array to read data from.</param>
+ /// <param name="off">How many bytes to skip at the beginning of the array.</param>
+ /// <param name="len">How many bytes to read from the array.</param>
+ public void AddData(byte[] buf, int off, int len)
+ {
+ if (m_readOnlyBuf)
+ throw new InvalidOperationException("Cannot add data to read-only buffer");
+
+ if ((m_skipped + m_available + len) > m_databuf.Length)
+ {
+ int desiredSize = ByteQueue.NextTwoPow(m_available + len);
+ if (desiredSize > m_databuf.Length)
+ {
+ byte[] tmp = new byte[desiredSize];
+ Array.Copy(m_databuf, m_skipped, tmp, 0, m_available);
+ m_databuf = tmp;
+ }
+ else
+ {
+ Array.Copy(m_databuf, m_skipped, m_databuf, 0, m_available);
+ }
+ m_skipped = 0;
+ }
+
+ Array.Copy(buf, off, m_databuf, m_skipped + m_available, len);
+ m_available += len;
+ }
+
+ /// <returns>The number of bytes which are available in this buffer.</returns>
+ public int Available
+ {
+ get { return m_available; }
+ }
+
+ /// <summary>Copy some bytes from the beginning of the data to the provided <see cref="Stream"/>.</summary>
+ /// <param name="output">The <see cref="Stream"/> to copy the bytes to.</param>
+ /// <param name="length">How many bytes to copy.</param>
+ public void CopyTo(Stream output, int length)
+ {
+ if (length > m_available)
+ throw new InvalidOperationException("Cannot copy " + length + " bytes, only got " + m_available);
+
+ output.Write(m_databuf, m_skipped, length);
+ }
+
+ /// <summary>Read data from the buffer.</summary>
+ /// <param name="buf">The buffer where the read data will be copied to.</param>
+ /// <param name="offset">How many bytes to skip at the beginning of buf.</param>
+ /// <param name="len">How many bytes to read at all.</param>
+ /// <param name="skip">How many bytes from our data to skip.</param>
+ public void Read(byte[] buf, int offset, int len, int skip)
+ {
+ if ((buf.Length - offset) < len)
+ {
+ throw new ArgumentException("Buffer size of " + buf.Length
+ + " is too small for a read of " + len + " bytes");
+ }
+ if ((m_available - skip) < len)
+ {
+ throw new InvalidOperationException("Not enough data to read");
+ }
+ Array.Copy(m_databuf, m_skipped + skip, buf, offset, len);
+ }
+
+ /// <summary>Return a <see cref="HandshakeMessageInput"/> over some bytes at the beginning of the data.
+ /// </summary>
+ /// <param name="length">How many bytes will be readable.</param>
+ /// <returns>A <see cref="HandshakeMessageInput"/> over the data.</returns>
+ internal HandshakeMessageInput ReadHandshakeMessage(int length)
+ {
+ if (length > m_available)
+ throw new InvalidOperationException("Cannot read " + length + " bytes, only got " + m_available);
+
+ int position = m_skipped;
+
+ m_available -= length;
+ m_skipped += length;
+
+ return new HandshakeMessageInput(m_databuf, position, length);
+ }
+
+ public int ReadInt32()
+ {
+ if (m_available < 4)
+ throw new InvalidOperationException("Not enough data to read");
+
+ return TlsUtilities.ReadInt32(m_databuf, m_skipped);
+ }
+
+ /// <summary>Remove some bytes from our data from the beginning.</summary>
+ /// <param name="i">How many bytes to remove.</param>
+ public void RemoveData(int i)
+ {
+ if (i > m_available)
+ throw new InvalidOperationException("Cannot remove " + i + " bytes, only got " + m_available);
+
+ /*
+ * Skip the data.
+ */
+ m_available -= i;
+ m_skipped += i;
+ }
+
+ /// <summary>Remove data from the buffer.</summary>
+ /// <param name="buf">The buffer where the removed data will be copied to.</param>
+ /// <param name="off">How many bytes to skip at the beginning of buf.</param>
+ /// <param name="len">How many bytes to read at all.</param>
+ /// <param name="skip">How many bytes from our data to skip.</param>
+ public void RemoveData(byte[] buf, int off, int len, int skip)
+ {
+ Read(buf, off, len, skip);
+ RemoveData(skip + len);
+ }
+
+ public byte[] RemoveData(int len, int skip)
+ {
+ byte[] buf = new byte[len];
+ RemoveData(buf, 0, len, skip);
+ return buf;
+ }
+
+ public void Shrink()
+ {
+ if (m_available == 0)
+ {
+ m_databuf = TlsUtilities.EmptyBytes;
+ m_skipped = 0;
+ }
+ else
+ {
+ int desiredSize = ByteQueue.NextTwoPow(m_available);
+ if (desiredSize < m_databuf.Length)
+ {
+ byte[] tmp = new byte[desiredSize];
+ Array.Copy(m_databuf, m_skipped, tmp, 0, m_available);
+ m_databuf = tmp;
+ m_skipped = 0;
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/ByteQueueInputStream.cs b/crypto/src/tls/ByteQueueInputStream.cs
new file mode 100644
index 000000000..b59b5d1e7
--- /dev/null
+++ b/crypto/src/tls/ByteQueueInputStream.cs
@@ -0,0 +1,72 @@
+using System;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class ByteQueueInputStream
+ : BaseInputStream
+ {
+ private readonly ByteQueue m_buffer;
+
+ public ByteQueueInputStream()
+ {
+ this.m_buffer = new ByteQueue();
+ }
+
+ public void AddBytes(byte[] buf)
+ {
+ m_buffer.AddData(buf, 0, buf.Length);
+ }
+
+ public void AddBytes(byte[] buf, int bufOff, int bufLen)
+ {
+ m_buffer.AddData(buf, bufOff, bufLen);
+ }
+
+ public int Peek(byte[] buf)
+ {
+ int bytesToRead = System.Math.Min(m_buffer.Available, buf.Length);
+ m_buffer.Read(buf, 0, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ public override int ReadByte()
+ {
+ if (m_buffer.Available == 0)
+ return -1;
+
+ return m_buffer.RemoveData(1, 0)[0];
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ int bytesToRead = System.Math.Min(m_buffer.Available, len);
+ m_buffer.RemoveData(buf, off, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ public long Skip(long n)
+ {
+ int bytesToRemove = System.Math.Min((int)n, m_buffer.Available);
+ m_buffer.RemoveData(bytesToRemove);
+ return bytesToRemove;
+ }
+
+ public int Available
+ {
+ get { return m_buffer.Available; }
+ }
+
+#if PORTABLE
+ //protected override void Dispose(bool disposing)
+ //{
+ // base.Dispose(disposing);
+ //}
+#else
+ public override void Close()
+ {
+ }
+#endif
+ }
+}
diff --git a/crypto/src/tls/ByteQueueOutputStream.cs b/crypto/src/tls/ByteQueueOutputStream.cs
new file mode 100644
index 000000000..76f04916f
--- /dev/null
+++ b/crypto/src/tls/ByteQueueOutputStream.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>OutputStream based on a ByteQueue implementation.</summary>
+ public sealed class ByteQueueOutputStream
+ : BaseOutputStream
+ {
+ private readonly ByteQueue m_buffer;
+
+ public ByteQueueOutputStream()
+ {
+ this.m_buffer = new ByteQueue();
+ }
+
+ public ByteQueue Buffer
+ {
+ get { return m_buffer; }
+ }
+
+ public override void WriteByte(byte b)
+ {
+ m_buffer.AddData(new byte[]{ b }, 0, 1);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ m_buffer.AddData(buf, off, len);
+ }
+ }
+}
diff --git a/crypto/src/tls/CachedInformationType.cs b/crypto/src/tls/CachedInformationType.cs
new file mode 100644
index 000000000..273efab0b
--- /dev/null
+++ b/crypto/src/tls/CachedInformationType.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class CachedInformationType
+ {
+ public const short cert = 1;
+ public const short cert_req = 2;
+
+ public static string GetName(short cachedInformationType)
+ {
+ switch (cachedInformationType)
+ {
+ case cert:
+ return "cert";
+ case cert_req:
+ return "cert_req";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short cachedInformationType)
+ {
+ return GetName(cachedInformationType) + "(" + cachedInformationType + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/CertChainType.cs b/crypto/src/tls/CertChainType.cs
new file mode 100644
index 000000000..8b989e8a2
--- /dev/null
+++ b/crypto/src/tls/CertChainType.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Implementation of the RFC 3546 3.3. CertChainType.</summary>
+ public abstract class CertChainType
+ {
+ public const short individual_certs = 0;
+ public const short pkipath = 1;
+
+ public static string GetName(short certChainType)
+ {
+ switch (certChainType)
+ {
+ case individual_certs:
+ return "individual_certs";
+ case pkipath:
+ return "pkipath";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short certChainType)
+ {
+ return GetName(certChainType) + "(" + certChainType + ")";
+ }
+
+ public static bool IsValid(short certChainType)
+ {
+ return certChainType >= individual_certs && certChainType <= pkipath;
+ }
+ }
+}
diff --git a/crypto/src/tls/Certificate.cs b/crypto/src/tls/Certificate.cs
new file mode 100644
index 000000000..fef35fc1e
--- /dev/null
+++ b/crypto/src/tls/Certificate.cs
@@ -0,0 +1,294 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Parsing and encoding of a <i>Certificate</i> struct from RFC 4346.</summary>
+ /// <remarks>
+ /// <pre>
+ /// opaque ASN.1Cert<2^24-1>;
+ /// struct {
+ /// ASN.1Cert certificate_list<0..2^24-1>;
+ /// } Certificate;
+ /// </pre>
+ /// </remarks>
+ public sealed class Certificate
+ {
+ private static readonly TlsCertificate[] EmptyCerts = new TlsCertificate[0];
+ private static readonly CertificateEntry[] EmptyCertEntries = new CertificateEntry[0];
+
+ public static readonly Certificate EmptyChain = new Certificate(EmptyCerts);
+ public static readonly Certificate EmptyChainTls13 = new Certificate(TlsUtilities.EmptyBytes, EmptyCertEntries);
+
+ public sealed class ParseOptions
+ {
+ private int m_maxChainLength = int.MaxValue;
+
+ public int MaxChainLength
+ {
+ get { return m_maxChainLength; }
+ }
+
+ public ParseOptions SetMaxChainLength(int maxChainLength)
+ {
+ this.m_maxChainLength = maxChainLength;
+ return this;
+ }
+ }
+
+ private static CertificateEntry[] Convert(TlsCertificate[] certificateList)
+ {
+ if (TlsUtilities.IsNullOrContainsNull(certificateList))
+ throw new ArgumentException("cannot be null or contain any nulls", "certificateList");
+
+ int count = certificateList.Length;
+ CertificateEntry[] result = new CertificateEntry[count];
+ for (int i = 0; i < count; ++i)
+ {
+ result[i] = new CertificateEntry(certificateList[i], null);
+ }
+ return result;
+ }
+
+ private readonly byte[] m_certificateRequestContext;
+ private readonly CertificateEntry[] m_certificateEntryList;
+
+ public Certificate(TlsCertificate[] certificateList)
+ : this(null, Convert(certificateList))
+ {
+ }
+
+ // TODO[tls13] Prefer to manage the certificateRequestContext internally only?
+ public Certificate(byte[] certificateRequestContext, CertificateEntry[] certificateEntryList)
+ {
+ if (null != certificateRequestContext && !TlsUtilities.IsValidUint8(certificateRequestContext.Length))
+ throw new ArgumentException("cannot be longer than 255", "certificateRequestContext");
+ if (TlsUtilities.IsNullOrContainsNull(certificateEntryList))
+ throw new ArgumentException("cannot be null or contain any nulls", "certificateEntryList");
+
+ this.m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
+ this.m_certificateEntryList = certificateEntryList;
+ }
+
+ public byte[] GetCertificateRequestContext()
+ {
+ return TlsUtilities.Clone(m_certificateRequestContext);
+ }
+
+ /// <returns>an array of <see cref="TlsCertificate"/> representing a certificate chain.</returns>
+ public TlsCertificate[] GetCertificateList()
+ {
+ return CloneCertificateList();
+ }
+
+ public TlsCertificate GetCertificateAt(int index)
+ {
+ return m_certificateEntryList[index].Certificate;
+ }
+
+ public CertificateEntry GetCertificateEntryAt(int index)
+ {
+ return m_certificateEntryList[index];
+ }
+
+ public CertificateEntry[] GetCertificateEntryList()
+ {
+ return CloneCertificateEntryList();
+ }
+
+ public short CertificateType
+ {
+ get { return Tls.CertificateType.X509; }
+ }
+
+ public int Length
+ {
+ get { return m_certificateEntryList.Length; }
+ }
+
+ /// <returns><c>true</c> if this certificate chain contains no certificates, or <c>false</c> otherwise.
+ /// </returns>
+ public bool IsEmpty
+ {
+ get { return m_certificateEntryList.Length == 0; }
+ }
+
+ /// <summary>Encode this <see cref="Certificate"/> to a <see cref="Stream"/>, and optionally calculate the
+ /// "end point hash" (per RFC 5929's tls-server-end-point binding).</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="messageOutput">the <see cref="Stream"/> to encode to.</param>
+ /// <param name="endPointHashOutput">the <see cref="Stream"/> to write the "end point hash" to (or null).
+ /// </param>
+ /// <exception cref="IOException"/>
+ public void Encode(TlsContext context, Stream messageOutput, Stream endPointHashOutput)
+ {
+ bool isTlsV13 = TlsUtilities.IsTlsV13(context);
+
+ if ((null != m_certificateRequestContext) != isTlsV13)
+ throw new InvalidOperationException();
+
+ if (isTlsV13)
+ {
+ TlsUtilities.WriteOpaque8(m_certificateRequestContext, messageOutput);
+ }
+
+ int count = m_certificateEntryList.Length;
+ IList certEncodings = Platform.CreateArrayList(count);
+ IList extEncodings = isTlsV13 ? Platform.CreateArrayList(count) : null;
+
+ long totalLength = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ CertificateEntry entry = m_certificateEntryList[i];
+ TlsCertificate cert = entry.Certificate;
+ byte[] derEncoding = cert.GetEncoded();
+
+ if (i == 0 && endPointHashOutput != null)
+ {
+ CalculateEndPointHash(context, cert, derEncoding, endPointHashOutput);
+ }
+
+ certEncodings.Add(derEncoding);
+ totalLength += derEncoding.Length;
+ totalLength += 3;
+
+ if (isTlsV13)
+ {
+ IDictionary extensions = entry.Extensions;
+ byte[] extEncoding = (null == extensions)
+ ? TlsUtilities.EmptyBytes
+ : TlsProtocol.WriteExtensionsData(extensions);
+
+ extEncodings.Add(extEncoding);
+ totalLength += extEncoding.Length;
+ totalLength += 2;
+ }
+ }
+
+ TlsUtilities.CheckUint24(totalLength);
+ TlsUtilities.WriteUint24((int)totalLength, messageOutput);
+
+ for (int i = 0; i < count; ++i)
+ {
+ byte[] certEncoding = (byte[])certEncodings[i];
+ TlsUtilities.WriteOpaque24(certEncoding, messageOutput);
+
+ if (isTlsV13)
+ {
+ byte[] extEncoding = (byte[])extEncodings[i];
+ TlsUtilities.WriteOpaque16(extEncoding, messageOutput);
+ }
+ }
+ }
+
+ /// <summary>Parse a <see cref="Certificate"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="options">the <see cref="ParseOptions"/> to apply during parsing.</param>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="messageInput">the <see cref="Stream"/> to parse from.</param>
+ /// <param name="endPointHashOutput">the <see cref="Stream"/> to write the "end point hash" to (or null).
+ /// </param>
+ /// <returns>a <see cref="Certificate"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static Certificate Parse(ParseOptions options, TlsContext context, Stream messageInput,
+ Stream endPointHashOutput)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ bool isTlsV13 = TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion);
+
+ byte[] certificateRequestContext = null;
+ if (isTlsV13)
+ {
+ certificateRequestContext = TlsUtilities.ReadOpaque8(messageInput);
+ }
+
+ int totalLength = TlsUtilities.ReadUint24(messageInput);
+ if (totalLength == 0)
+ {
+ return !isTlsV13 ? EmptyChain
+ : certificateRequestContext.Length < 1 ? EmptyChainTls13
+ : new Certificate(certificateRequestContext, EmptyCertEntries);
+ }
+
+ byte[] certListData = TlsUtilities.ReadFully(totalLength, messageInput);
+ MemoryStream buf = new MemoryStream(certListData, false);
+
+ TlsCrypto crypto = context.Crypto;
+ int maxChainLength = System.Math.Max(1, options.MaxChainLength);
+
+ IList certificate_list = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ if (certificate_list.Count >= maxChainLength)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error,
+ "Certificate chain longer than maximum (" + maxChainLength + ")");
+ }
+
+ byte[] derEncoding = TlsUtilities.ReadOpaque24(buf, 1);
+ TlsCertificate cert = crypto.CreateCertificate(derEncoding);
+
+ if (certificate_list.Count < 1 && endPointHashOutput != null)
+ {
+ CalculateEndPointHash(context, cert, derEncoding, endPointHashOutput);
+ }
+
+ IDictionary extensions = null;
+ if (isTlsV13)
+ {
+ byte[] extEncoding = TlsUtilities.ReadOpaque16(buf);
+
+ extensions = TlsProtocol.ReadExtensionsData13(HandshakeType.certificate, extEncoding);
+ }
+
+ certificate_list.Add(new CertificateEntry(cert, extensions));
+ }
+
+ CertificateEntry[] certificateList = new CertificateEntry[certificate_list.Count];
+ for (int i = 0; i < certificate_list.Count; i++)
+ {
+ certificateList[i] = (CertificateEntry)certificate_list[i];
+ }
+
+ return new Certificate(certificateRequestContext, certificateList);
+ }
+
+ private static void CalculateEndPointHash(TlsContext context, TlsCertificate cert, byte[] encoding,
+ Stream output)
+ {
+ byte[] endPointHash = TlsUtilities.CalculateEndPointHash(context, cert, encoding);
+ if (endPointHash != null && endPointHash.Length > 0)
+ {
+ output.Write(endPointHash, 0, endPointHash.Length);
+ }
+ }
+
+ private TlsCertificate[] CloneCertificateList()
+ {
+ int count = m_certificateEntryList.Length;
+ if (0 == count)
+ return EmptyCerts;
+
+ TlsCertificate[] result = new TlsCertificate[count];
+ for (int i = 0; i < count; ++i)
+ {
+ result[i] = m_certificateEntryList[i].Certificate;
+ }
+ return result;
+ }
+
+ private CertificateEntry[] CloneCertificateEntryList()
+ {
+ int count = m_certificateEntryList.Length;
+ if (0 == count)
+ return EmptyCertEntries;
+
+ CertificateEntry[] result = new CertificateEntry[count];
+ Array.Copy(m_certificateEntryList, 0, result, 0, count);
+ return result;
+ }
+ }
+}
diff --git a/crypto/src/tls/CertificateEntry.cs b/crypto/src/tls/CertificateEntry.cs
new file mode 100644
index 000000000..b88677536
--- /dev/null
+++ b/crypto/src/tls/CertificateEntry.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class CertificateEntry
+ {
+ private readonly TlsCertificate m_certificate;
+ private readonly IDictionary m_extensions;
+
+ public CertificateEntry(TlsCertificate certificate, IDictionary extensions)
+ {
+ if (null == certificate)
+ throw new ArgumentNullException("certificate");
+
+ this.m_certificate = certificate;
+ this.m_extensions = extensions;
+ }
+
+ public TlsCertificate Certificate
+ {
+ get { return m_certificate; }
+ }
+
+ public IDictionary Extensions
+ {
+ get { return m_extensions; }
+ }
+ }
+}
diff --git a/crypto/src/tls/CertificateRequest.cs b/crypto/src/tls/CertificateRequest.cs
new file mode 100644
index 000000000..1abf01aed
--- /dev/null
+++ b/crypto/src/tls/CertificateRequest.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Parsing and encoding of a <i>CertificateRequest</i> struct from RFC 4346.</summary>
+ /// <remarks>
+ /// <pre>
+ /// struct {
+ /// ClientCertificateType certificate_types<1..2^8-1>;
+ /// DistinguishedName certificate_authorities<3..2^16-1>;
+ /// } CertificateRequest;
+ /// </pre>
+ /// Updated for RFC 5246:
+ /// <pre>
+ /// struct {
+ /// ClientCertificateType certificate_types <1..2 ^ 8 - 1>;
+ /// SignatureAndHashAlgorithm supported_signature_algorithms <2 ^ 16 - 1>;
+ /// DistinguishedName certificate_authorities <0..2 ^ 16 - 1>;
+ /// } CertificateRequest;
+ /// </pre>
+ /// Revised for RFC 8446:
+ /// <pre>
+ /// struct {
+ /// opaque certificate_request_context <0..2 ^ 8 - 1>;
+ /// Extension extensions <2..2 ^ 16 - 1>;
+ /// } CertificateRequest;
+ /// </pre>
+ /// </remarks>
+ /// <seealso cref="ClientCertificateType"/>
+ /// <seealso cref="X509Name"/>
+ public sealed class CertificateRequest
+ {
+ /// <exception cref="IOException"/>
+ private static IList CheckSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms,
+ short alertDescription)
+ {
+ if (null == supportedSignatureAlgorithms)
+ throw new TlsFatalAlert(alertDescription, "'signature_algorithms' is required");
+
+ return supportedSignatureAlgorithms;
+ }
+
+ private readonly byte[] m_certificateRequestContext;
+ private readonly short[] m_certificateTypes;
+ private readonly IList m_supportedSignatureAlgorithms;
+ private readonly IList m_supportedSignatureAlgorithmsCert;
+ private readonly IList m_certificateAuthorities;
+
+ /// <param name="certificateTypes">see <see cref="ClientCertificateType"/> for valid constants.</param>
+ /// <param name="supportedSignatureAlgorithms"></param>
+ /// <param name="certificateAuthorities">an <see cref="IList"/> of <see cref="X509Name"/>.</param>
+ public CertificateRequest(short[] certificateTypes, IList supportedSignatureAlgorithms,
+ IList certificateAuthorities)
+ : this(null, certificateTypes, supportedSignatureAlgorithms, null, certificateAuthorities)
+ {
+ }
+
+ // TODO[tls13] Prefer to manage the certificateRequestContext internally only?
+ /// <exception cref="IOException"/>
+ public CertificateRequest(byte[] certificateRequestContext, IList supportedSignatureAlgorithms,
+ IList supportedSignatureAlgorithmsCert, IList certificateAuthorities)
+ : this(certificateRequestContext, null,
+ CheckSupportedSignatureAlgorithms(supportedSignatureAlgorithms, AlertDescription.internal_error),
+ supportedSignatureAlgorithmsCert, certificateAuthorities)
+ {
+ /*
+ * TODO[tls13] Removed certificateTypes, added certificate_request_context, added extensions
+ * (required: signature_algorithms, optional: status_request, signed_certificate_timestamp,
+ * certificate_authorities, oid_filters, signature_algorithms_cert)
+ */
+ }
+
+ private CertificateRequest(byte[] certificateRequestContext, short[] certificateTypes,
+ IList supportedSignatureAlgorithms, IList supportedSignatureAlgorithmsCert, IList certificateAuthorities)
+ {
+ if (null != certificateRequestContext && !TlsUtilities.IsValidUint8(certificateRequestContext.Length))
+ throw new ArgumentException("cannot be longer than 255", "certificateRequestContext");
+ if (null != certificateTypes
+ && (certificateTypes.Length < 1 || !TlsUtilities.IsValidUint8(certificateTypes.Length)))
+ {
+ throw new ArgumentException("should have length from 1 to 255", "certificateTypes");
+ }
+
+ this.m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
+ this.m_certificateTypes = certificateTypes;
+ this.m_supportedSignatureAlgorithms = supportedSignatureAlgorithms;
+ this.m_supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert;
+ this.m_certificateAuthorities = certificateAuthorities;
+ }
+
+ public byte[] GetCertificateRequestContext()
+ {
+ return TlsUtilities.Clone(m_certificateRequestContext);
+ }
+
+ /// <returns>an array of certificate types</returns>
+ /// <seealso cref="ClientCertificateType"/>
+ public short[] CertificateTypes
+ {
+ get { return m_certificateTypes; }
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="SignatureAndHashAlgorithm"/> (or null before TLS 1.2).
+ /// </returns>
+ public IList SupportedSignatureAlgorithms
+ {
+ get { return m_supportedSignatureAlgorithms; }
+ }
+
+ /// <returns>an optional <see cref="IList"/> of <see cref="SignatureAndHashAlgorithm"/>. May be non-null from
+ /// TLS 1.3 onwards.</returns>
+ public IList SupportedSignatureAlgorithmsCert
+ {
+ get { return m_supportedSignatureAlgorithmsCert; }
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="X509Name"/>.</returns>
+ public IList CertificateAuthorities
+ {
+ get { return m_certificateAuthorities; }
+ }
+
+ public bool HasCertificateRequestContext(byte[] certificateRequestContext)
+ {
+ return Arrays.AreEqual(m_certificateRequestContext, certificateRequestContext);
+ }
+
+ /// <summary>Encode this <see cref="CertificateRequest"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(TlsContext context, Stream output)
+ {
+ ProtocolVersion negotiatedVersion = context.ServerVersion;
+ bool isTlsV12 = TlsUtilities.IsTlsV12(negotiatedVersion);
+ bool isTlsV13 = TlsUtilities.IsTlsV13(negotiatedVersion);
+
+ if (isTlsV13 != (null != m_certificateRequestContext) ||
+ isTlsV13 != (null == m_certificateTypes) ||
+ isTlsV12 != (null != m_supportedSignatureAlgorithms) ||
+ (!isTlsV13 && (null != m_supportedSignatureAlgorithmsCert)))
+ {
+ throw new InvalidOperationException();
+ }
+
+ if (isTlsV13)
+ {
+ TlsUtilities.WriteOpaque8(m_certificateRequestContext, output);
+
+ IDictionary extensions = Platform.CreateHashtable();
+ TlsExtensionsUtilities.AddSignatureAlgorithmsExtension(extensions, m_supportedSignatureAlgorithms);
+
+ if (null != m_supportedSignatureAlgorithmsCert)
+ {
+ TlsExtensionsUtilities.AddSignatureAlgorithmsCertExtension(extensions,
+ m_supportedSignatureAlgorithmsCert);
+ }
+
+ if (null != m_certificateAuthorities)
+ {
+ TlsExtensionsUtilities.AddCertificateAuthoritiesExtension(extensions, m_certificateAuthorities);
+ }
+
+ byte[] extEncoding = TlsProtocol.WriteExtensionsData(extensions);
+
+ TlsUtilities.WriteOpaque16(extEncoding, output);
+ return;
+ }
+
+ TlsUtilities.WriteUint8ArrayWithUint8Length(m_certificateTypes, output);
+
+ if (isTlsV12)
+ {
+ // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+ TlsUtilities.EncodeSupportedSignatureAlgorithms(m_supportedSignatureAlgorithms, output);
+ }
+
+ if (m_certificateAuthorities == null || m_certificateAuthorities.Count < 1)
+ {
+ TlsUtilities.WriteUint16(0, output);
+ }
+ else
+ {
+ IList derEncodings = Platform.CreateArrayList(m_certificateAuthorities.Count);
+
+ int totalLength = 0;
+ foreach (X509Name certificateAuthority in m_certificateAuthorities)
+ {
+ byte[] derEncoding = certificateAuthority.GetEncoded(Asn1Encodable.Der);
+ derEncodings.Add(derEncoding);
+ totalLength += derEncoding.Length + 2;
+ }
+
+ TlsUtilities.CheckUint16(totalLength);
+ TlsUtilities.WriteUint16(totalLength, output);
+
+ foreach (byte[] derEncoding in derEncodings)
+ {
+ TlsUtilities.WriteOpaque16(derEncoding, output);
+ }
+ }
+ }
+
+ /// <summary>Parse a <see cref="CertificateRequest"/> from a <see cref="Stream"/></summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="CertificateRequest"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static CertificateRequest Parse(TlsContext context, Stream input)
+ {
+ ProtocolVersion negotiatedVersion = context.ServerVersion;
+ bool isTlsV13 = TlsUtilities.IsTlsV13(negotiatedVersion);
+
+ if (isTlsV13)
+ {
+ byte[] certificateRequestContext = TlsUtilities.ReadOpaque8(input);
+
+ /*
+ * TODO[tls13] required: signature_algorithms; optional: status_request,
+ * signed_certificate_timestamp, certificate_authorities, oid_filters,
+ * signature_algorithms_cert
+ */
+
+ byte[] extEncoding = TlsUtilities.ReadOpaque16(input);
+
+ IDictionary extensions = TlsProtocol.ReadExtensionsData13(HandshakeType.certificate_request,
+ extEncoding);
+
+ IList supportedSignatureAlgorithms13 = CheckSupportedSignatureAlgorithms(
+ TlsExtensionsUtilities.GetSignatureAlgorithmsExtension(extensions),
+ AlertDescription.missing_extension);
+ IList supportedSignatureAlgorithmsCert13 = TlsExtensionsUtilities
+ .GetSignatureAlgorithmsCertExtension(extensions);
+ IList certificateAuthorities13 = TlsExtensionsUtilities.GetCertificateAuthoritiesExtension(extensions);
+
+ return new CertificateRequest(certificateRequestContext, supportedSignatureAlgorithms13,
+ supportedSignatureAlgorithmsCert13, certificateAuthorities13);
+ }
+
+ bool isTLSv12 = TlsUtilities.IsTlsV12(negotiatedVersion);
+
+ short[] certificateTypes = TlsUtilities.ReadUint8ArrayWithUint8Length(input, 1);
+
+ IList supportedSignatureAlgorithms = null;
+ if (isTLSv12)
+ {
+ supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(input);
+ }
+
+ IList certificateAuthorities = null;
+ {
+ byte[] certAuthData = TlsUtilities.ReadOpaque16(input);
+ if (certAuthData.Length > 0)
+ {
+ certificateAuthorities = Platform.CreateArrayList();
+ MemoryStream bis = new MemoryStream(certAuthData, false);
+ do
+ {
+ byte[] derEncoding = TlsUtilities.ReadOpaque16(bis, 1);
+ Asn1Object asn1 = TlsUtilities.ReadDerObject(derEncoding);
+ certificateAuthorities.Add(X509Name.GetInstance(asn1));
+ }
+ while (bis.Position < bis.Length);
+ }
+ }
+
+ return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities);
+ }
+ }
+}
diff --git a/crypto/src/tls/CertificateStatus.cs b/crypto/src/tls/CertificateStatus.cs
new file mode 100644
index 000000000..61f4336a8
--- /dev/null
+++ b/crypto/src/tls/CertificateStatus.cs
@@ -0,0 +1,220 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Ocsp;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class CertificateStatus
+ {
+ private readonly short m_statusType;
+ private readonly object m_response;
+
+ public CertificateStatus(short statusType, object response)
+ {
+ if (!IsCorrectType(statusType, response))
+ throw new ArgumentException("not an instance of the correct type", "response");
+
+ this.m_statusType = statusType;
+ this.m_response = response;
+ }
+
+ public short StatusType
+ {
+ get { return m_statusType; }
+ }
+
+ public object Response
+ {
+ get { return m_response; }
+ }
+
+ public OcspResponse OcspResponse
+ {
+ get
+ {
+ if (!IsCorrectType(CertificateStatusType.ocsp, m_response))
+ throw new InvalidOperationException("'response' is not an OCSPResponse");
+
+ return (OcspResponse)m_response;
+ }
+ }
+
+ /// <summary>an <see cref="IList"/> of (possibly null) <see cref="Asn1.Ocsp.OcspResponse"/>.</summary>
+ public IList OcspResponseList
+ {
+ get
+ {
+ if (!IsCorrectType(CertificateStatusType.ocsp_multi, m_response))
+ throw new InvalidOperationException("'response' is not an OCSPResponseList");
+
+ return (IList)m_response;
+ }
+ }
+
+ /// <summary>Encode this <see cref="CertificateStatus"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_statusType, output);
+
+ switch (m_statusType)
+ {
+ case CertificateStatusType.ocsp:
+ {
+ OcspResponse ocspResponse = (OcspResponse)m_response;
+ byte[] derEncoding = ocspResponse.GetEncoded(Asn1Encodable.Der);
+ TlsUtilities.WriteOpaque24(derEncoding, output);
+ break;
+ }
+ case CertificateStatusType.ocsp_multi:
+ {
+ IList ocspResponseList = (IList)m_response;
+ int count = ocspResponseList.Count;
+
+ IList derEncodings = Platform.CreateArrayList(count);
+ long totalLength = 0;
+ foreach (OcspResponse ocspResponse in ocspResponseList)
+ {
+ if (ocspResponse == null)
+ {
+ derEncodings.Add(TlsUtilities.EmptyBytes);
+ }
+ else
+ {
+ byte[] derEncoding = ocspResponse.GetEncoded(Asn1Encodable.Der);
+ derEncodings.Add(derEncoding);
+ totalLength += derEncoding.Length;
+ }
+ totalLength += 3;
+ }
+
+ TlsUtilities.CheckUint24(totalLength);
+ TlsUtilities.WriteUint24((int)totalLength, output);
+
+ foreach (byte[] derEncoding in derEncodings)
+ {
+ TlsUtilities.WriteOpaque24(derEncoding, output);
+ }
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /// <summary>Parse a <see cref="CertificateStatus"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="CertificateStatus"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static CertificateStatus Parse(TlsContext context, Stream input)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ Certificate peerCertificate = securityParameters.PeerCertificate;
+ if (null == peerCertificate || peerCertificate.IsEmpty
+ || CertificateType.X509 != peerCertificate.CertificateType)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ int certificateCount = peerCertificate.Length;
+ int statusRequestVersion = securityParameters.StatusRequestVersion;
+
+ short status_type = TlsUtilities.ReadUint8(input);
+ object response;
+
+ switch (status_type)
+ {
+ case CertificateStatusType.ocsp:
+ {
+ RequireStatusRequestVersion(1, statusRequestVersion);
+
+ byte[] derEncoding = TlsUtilities.ReadOpaque24(input, 1);
+ Asn1Object derObject = TlsUtilities.ReadDerObject(derEncoding);
+ response = OcspResponse.GetInstance(derObject);
+ break;
+ }
+ case CertificateStatusType.ocsp_multi:
+ {
+ RequireStatusRequestVersion(2, statusRequestVersion);
+
+ byte[] ocsp_response_list = TlsUtilities.ReadOpaque24(input, 1);
+ MemoryStream buf = new MemoryStream(ocsp_response_list, false);
+
+ IList ocspResponseList = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ if (ocspResponseList.Count >= certificateCount)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ int length = TlsUtilities.ReadUint24(buf);
+ if (length < 1)
+ {
+ ocspResponseList.Add(null);
+ }
+ else
+ {
+ byte[] derEncoding = TlsUtilities.ReadFully(length, buf);
+ Asn1Object derObject = TlsUtilities.ReadDerObject(derEncoding);
+ OcspResponse ocspResponse = OcspResponse.GetInstance(derObject);
+ ocspResponseList.Add(ocspResponse);
+ }
+ }
+
+ // Match IList capacity to actual size
+ response = Platform.CreateArrayList(ocspResponseList);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new CertificateStatus(status_type, response);
+ }
+
+ private static bool IsCorrectType(short statusType, Object response)
+ {
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ return response is OcspResponse;
+ case CertificateStatusType.ocsp_multi:
+ return IsOcspResponseList(response);
+ default:
+ throw new ArgumentException("unsupported CertificateStatusType", "statusType");
+ }
+ }
+
+ private static bool IsOcspResponseList(object response)
+ {
+ if (!(response is IList))
+ return false;
+
+ IList v = (IList)response;
+ int count = v.Count;
+ if (count < 1)
+ return false;
+
+ foreach (object e in v)
+ {
+ if (null != e && !(e is OcspResponse))
+ return false;
+ }
+ return true;
+ }
+
+ /// <exception cref="IOException"/>
+ private static void RequireStatusRequestVersion(int minVersion, int statusRequestVersion)
+ {
+ if (statusRequestVersion < minVersion)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ }
+}
diff --git a/crypto/src/tls/CertificateStatusRequest.cs b/crypto/src/tls/CertificateStatusRequest.cs
new file mode 100644
index 000000000..4731316cd
--- /dev/null
+++ b/crypto/src/tls/CertificateStatusRequest.cs
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Implementation of the RFC 3546 3.6. CertificateStatusRequest.</summary>
+ public sealed class CertificateStatusRequest
+ {
+ private short m_statusType;
+ private object m_request;
+
+ public CertificateStatusRequest(short statusType, object request)
+ {
+ if (!IsCorrectType(statusType, request))
+ throw new ArgumentException("not an instance of the correct type", "request");
+
+ this.m_statusType = statusType;
+ this.m_request = request;
+ }
+
+ public short StatusType
+ {
+ get { return m_statusType; }
+ }
+
+ public object Request
+ {
+ get { return m_request; }
+ }
+
+ public OcspStatusRequest OcspStatusRequest
+ {
+ get
+ {
+ if (!IsCorrectType(CertificateStatusType.ocsp, m_request))
+ throw new InvalidOperationException("'request' is not an OCSPStatusRequest");
+
+ return (OcspStatusRequest)m_request;
+ }
+ }
+
+ /// <summary>Encode this <see cref="CertificateStatusRequest"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_statusType, output);
+
+ switch (m_statusType)
+ {
+ case CertificateStatusType.ocsp:
+ ((OcspStatusRequest)m_request).Encode(output);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /// <summary>Parse a <see cref="CertificateStatusRequest"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="CertificateStatusRequest"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static CertificateStatusRequest Parse(Stream input)
+ {
+ short status_type = TlsUtilities.ReadUint8(input);
+ object request;
+
+ switch (status_type)
+ {
+ case CertificateStatusType.ocsp:
+ request = OcspStatusRequest.Parse(input);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new CertificateStatusRequest(status_type, request);
+ }
+
+ private static bool IsCorrectType(short statusType, object request)
+ {
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ return request is OcspStatusRequest;
+ default:
+ throw new ArgumentException("unsupported CertificateStatusType", "statusType");
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/CertificateStatusRequestItemV2.cs b/crypto/src/tls/CertificateStatusRequestItemV2.cs
new file mode 100644
index 000000000..a30d9ad9f
--- /dev/null
+++ b/crypto/src/tls/CertificateStatusRequestItemV2.cs
@@ -0,0 +1,100 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Implementation of the RFC 6961 2.2. CertificateStatusRequestItemV2.</summary>
+ public sealed class CertificateStatusRequestItemV2
+ {
+ private readonly short m_statusType;
+ private readonly object m_request;
+
+ public CertificateStatusRequestItemV2(short statusType, object request)
+ {
+ if (!IsCorrectType(statusType, request))
+ throw new ArgumentException("not an instance of the correct type", "request");
+
+ this.m_statusType = statusType;
+ this.m_request = request;
+ }
+
+ public short StatusType
+ {
+ get { return m_statusType; }
+ }
+
+ public object Request
+ {
+ get { return m_request; }
+ }
+
+ public OcspStatusRequest OcspStatusRequest
+ {
+ get
+ {
+ if (!(m_request is OcspStatusRequest))
+ throw new InvalidOperationException("'request' is not an OcspStatusRequest");
+
+ return (OcspStatusRequest)m_request;
+ }
+ }
+
+ /// <summary>Encode this <see cref="CertificateStatusRequestItemV2"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_statusType, output);
+
+ MemoryStream buf = new MemoryStream();
+ switch (m_statusType)
+ {
+ case CertificateStatusType.ocsp:
+ case CertificateStatusType.ocsp_multi:
+ ((OcspStatusRequest)m_request).Encode(buf);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ byte[] requestBytes = buf.ToArray();
+ TlsUtilities.WriteOpaque16(requestBytes, output);
+ }
+
+ /// <summary>Parse a <see cref="CertificateStatusRequestItemV2"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="CertificateStatusRequestItemV2"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static CertificateStatusRequestItemV2 Parse(Stream input)
+ {
+ short status_type = TlsUtilities.ReadUint8(input);
+
+ object request;
+ byte[] requestBytes = TlsUtilities.ReadOpaque16(input);
+ MemoryStream buf = new MemoryStream(requestBytes, false);
+ switch (status_type)
+ {
+ case CertificateStatusType.ocsp:
+ case CertificateStatusType.ocsp_multi:
+ request = OcspStatusRequest.Parse(buf);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ TlsProtocol.AssertEmpty(buf);
+
+ return new CertificateStatusRequestItemV2(status_type, request);
+ }
+
+ private static bool IsCorrectType(short statusType, object request)
+ {
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ case CertificateStatusType.ocsp_multi:
+ return request is OcspStatusRequest;
+ default:
+ throw new ArgumentException("unsupported CertificateStatusType", "statusType");
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/CertificateStatusType.cs b/crypto/src/tls/CertificateStatusType.cs
new file mode 100644
index 000000000..bb0c42455
--- /dev/null
+++ b/crypto/src/tls/CertificateStatusType.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class CertificateStatusType
+ {
+ /*
+ * RFC 6066
+ */
+ public const short ocsp = 1;
+
+ /*
+ * RFC 6961
+ */
+ public const short ocsp_multi = 2;
+ }
+}
diff --git a/crypto/src/tls/CertificateType.cs b/crypto/src/tls/CertificateType.cs
new file mode 100644
index 000000000..eed302416
--- /dev/null
+++ b/crypto/src/tls/CertificateType.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 6091</summary>
+ public abstract class CertificateType
+ {
+ public const short X509 = 0;
+ public const short OpenPGP = 1;
+
+ /*
+ * RFC 7250
+ */
+ public const short RawPublicKey = 2;
+ }
+}
diff --git a/crypto/src/tls/CertificateUrl.cs b/crypto/src/tls/CertificateUrl.cs
new file mode 100644
index 000000000..d2445772a
--- /dev/null
+++ b/crypto/src/tls/CertificateUrl.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 3546 3.3</summary>
+ public sealed class CertificateUrl
+ {
+ private readonly short m_type;
+ private readonly IList m_urlAndHashList;
+
+ /// <param name="type">see <see cref="CertChainType"/> for valid constants.</param>
+ /// <param name="urlAndHashList">an <see cref="IList"/> of <see cref="UrlAndHash"/>.</param>
+ public CertificateUrl(short 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");
+ if (type == CertChainType.pkipath && urlAndHashList.Count != 1)
+ throw new ArgumentException("must contain exactly one entry when type is "
+ + CertChainType.GetText(type), "urlAndHashList");
+
+ this.m_type = type;
+ this.m_urlAndHashList = urlAndHashList;
+ }
+
+ /// <returns><see cref="CertChainType"/></returns>
+ public short Type
+ {
+ get { return m_type; }
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="UrlAndHash"/>.</returns>
+ public IList UrlAndHashList
+ {
+ get { return m_urlAndHashList; }
+ }
+
+ /// <summary>Encode this <see cref="CertificateUrl"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_type, output);
+
+ ListBuffer16 buf = new ListBuffer16();
+ foreach (UrlAndHash urlAndHash in m_urlAndHashList)
+ {
+ urlAndHash.Encode(buf);
+ }
+ buf.EncodeTo(output);
+ }
+
+ /// <summary>Parse a <see cref="CertificateUrl"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="CertificateUrl"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static CertificateUrl Parse(TlsContext context, Stream input)
+ {
+ short 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);
+ }
+
+ if (type == CertChainType.pkipath && url_and_hash_list.Count != 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ 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
+ int length = (int)Length - 2;
+ TlsUtilities.CheckUint16(length);
+
+ Seek(0L, SeekOrigin.Begin);
+ TlsUtilities.WriteUint16(length, this);
+
+ Streams.WriteBufTo(this, output);
+
+ Platform.Dispose(this);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/ChangeCipherSpec.cs b/crypto/src/tls/ChangeCipherSpec.cs
new file mode 100644
index 000000000..f83fdb499
--- /dev/null
+++ b/crypto/src/tls/ChangeCipherSpec.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class ChangeCipherSpec
+ {
+ public const short change_cipher_spec = 1;
+ }
+}
diff --git a/crypto/src/tls/ChannelBinding.cs b/crypto/src/tls/ChannelBinding.cs
new file mode 100644
index 000000000..84f8bc4df
--- /dev/null
+++ b/crypto/src/tls/ChannelBinding.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5056</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>
+ public abstract class ChannelBinding
+ {
+ /*
+ * RFC 5929
+ */
+ public const int tls_server_end_point = 0;
+ public const int tls_unique = 1;
+ public const int tls_unique_for_telnet = 2;
+ }
+}
diff --git a/crypto/src/tls/CipherSuite.cs b/crypto/src/tls/CipherSuite.cs
new file mode 100644
index 000000000..5c53268e2
--- /dev/null
+++ b/crypto/src/tls/CipherSuite.cs
@@ -0,0 +1,461 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 2246 A.5</summary>
+ public abstract class CipherSuite
+ {
+ public static bool IsScsv(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case TLS_EMPTY_RENEGOTIATION_INFO_SCSV:
+ case TLS_FALLBACK_SCSV:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public const int TLS_NULL_WITH_NULL_NULL = 0x0000;
+ public const int TLS_RSA_WITH_NULL_MD5 = 0x0001;
+ public const int TLS_RSA_WITH_NULL_SHA = 0x0002;
+ public const int TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003;
+ public const int TLS_RSA_WITH_RC4_128_MD5 = 0x0004;
+ public const int TLS_RSA_WITH_RC4_128_SHA = 0x0005;
+ public const int TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006;
+ public const int TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007;
+ public const int TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008;
+ public const int TLS_RSA_WITH_DES_CBC_SHA = 0x0009;
+ public const int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A;
+ public const int TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B;
+ public const int TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C;
+ public const int TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D;
+ public const int TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E;
+ public const int TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F;
+ public const int TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010;
+ public const int TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011;
+ public const int TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012;
+ public const int TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013;
+ public const int TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014;
+ public const int TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015;
+ public const int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016;
+ public const int TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017;
+ public const int TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018;
+ public const int TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019;
+ public const int TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A;
+ public const int TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B;
+
+ /*
+ * Note: The cipher suite values { 0x00, 0x1C } and { 0x00, 0x1D } are reserved to avoid
+ * collision with Fortezza-based cipher suites in SSL 3.
+ */
+
+ /*
+ * RFC 3268
+ */
+ public const int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F;
+ public const int TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030;
+ public const int TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031;
+ public const int TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032;
+ public const int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033;
+ public const int TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034;
+ public const int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035;
+ public const int TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036;
+ public const int TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037;
+ public const int TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038;
+ public const int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039;
+ public const int TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A;
+
+ /*
+ * 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;
+ public const int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043;
+ 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;
+ public const int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087;
+ 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
+ */
+ public const int TLS_RSA_WITH_SEED_CBC_SHA = 0x0096;
+ public const int TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097;
+ public const int TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098;
+ public const int TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099;
+ public const int TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A;
+ public const int TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B;
+
+ /*
+ * RFC 4279
+ */
+ public const int TLS_PSK_WITH_RC4_128_SHA = 0x008A;
+ public const int TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B;
+ public const int TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C;
+ public const int TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D;
+ public const int TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E;
+ public const int TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F;
+ public const int TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090;
+ public const int TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091;
+ public const int TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092;
+ public const int TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093;
+ public const int TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094;
+ public const int TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095;
+
+ /*
+ * RFC 4492
+ */
+ public const int TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001;
+ public const int TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002;
+ public const int TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003;
+ public const int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004;
+ public const int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005;
+ public const int TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006;
+ public const int TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007;
+ public const int TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008;
+ public const int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009;
+ public const int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A;
+ public const int TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B;
+ public const int TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C;
+ public const int TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D;
+ public const int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E;
+ public const int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F;
+ public const int TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010;
+ public const int TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011;
+ public const int TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012;
+ public const int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013;
+ public const int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014;
+ public const int TLS_ECDH_anon_WITH_NULL_SHA = 0xC015;
+ public const int TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016;
+ public const int TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017;
+ public const int TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018;
+ public const int TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019;
+
+ /*
+ * RFC 4785
+ */
+ public const int TLS_PSK_WITH_NULL_SHA = 0x002C;
+ public const int TLS_DHE_PSK_WITH_NULL_SHA = 0x002D;
+ public const int TLS_RSA_PSK_WITH_NULL_SHA = 0x002E;
+
+ /*
+ * RFC 5054
+ */
+ public const int TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A;
+ public const int TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B;
+ public const int TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C;
+ public const int TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D;
+ public const int TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E;
+ public const int TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F;
+ public const int TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020;
+ public const int TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021;
+ public const int TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022;
+
+ /*
+ * RFC 5246
+ */
+ public const int TLS_RSA_WITH_NULL_SHA256 = 0x003B;
+ public const int TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C;
+ public const int TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D;
+ public const int TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E;
+ public const int TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F;
+ public const int TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040;
+ public const int TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067;
+ public const int TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068;
+ public const int TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069;
+ public const int TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A;
+ public const int TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B;
+ public const int TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C;
+ public const int TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D;
+
+ /*
+ * RFC 5288
+ */
+ public const int TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C;
+ public const int TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D;
+ public const int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E;
+ public const int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F;
+ public const int TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0;
+ public const int TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1;
+ public const int TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2;
+ public const int TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3;
+ public const int TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4;
+ public const int TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5;
+ public const int TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6;
+ public const int TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7;
+
+ /*
+ * RFC 5289
+ */
+ public const int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023;
+ public const int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024;
+ public const int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025;
+ public const int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026;
+ public const int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027;
+ public const int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028;
+ public const int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029;
+ public const int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A;
+ public const int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B;
+ public const int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C;
+ public const int TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D;
+ public const int TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E;
+ public const int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F;
+ public const int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030;
+ public const int TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031;
+ public const int TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032;
+
+ /*
+ * RFC 5487
+ */
+ public const int TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8;
+ public const int TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9;
+ public const int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA;
+ public const int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB;
+ public const int TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC;
+ public const int TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD;
+ public const int TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE;
+ public const int TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF;
+ public const int TLS_PSK_WITH_NULL_SHA256 = 0x00B0;
+ public const int TLS_PSK_WITH_NULL_SHA384 = 0x00B1;
+ public const int TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2;
+ public const int TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3;
+ public const int TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4;
+ public const int TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5;
+ public const int TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6;
+ public const int TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7;
+ public const int TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8;
+ public const int TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9;
+
+ /*
+ * RFC 5489
+ */
+ public const int TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033;
+ public const int TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034;
+ public const int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035;
+ public const int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036;
+ public const int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037;
+ public const int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038;
+ public const int TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039;
+ public const int TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A;
+ public const int TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B;
+
+ /*
+ * RFC 5746
+ */
+ public const int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
+
+ /*
+ * RFC 6209
+ */
+ public const int TLS_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC03C;
+ public const int TLS_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC03D;
+ public const int TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC03E;
+ public const int TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC03F;
+ public const int TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC040;
+ public const int TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC041;
+ public const int TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC042;
+ public const int TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC043;
+ public const int TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC044;
+ public const int TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC045;
+ public const int TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 = 0xC046;
+ public const int TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 = 0xC047;
+
+ public const int TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC048;
+ public const int TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC049;
+ public const int TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC04A;
+ public const int TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC04B;
+ public const int TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04C;
+ public const int TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04D;
+ public const int TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04E;
+ public const int TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04F;
+
+ public const int TLS_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC050;
+ public const int TLS_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC051;
+ public const int TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC052;
+ public const int TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC053;
+ public const int TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC054;
+ public const int TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC055;
+ public const int TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC056;
+ public const int TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC057;
+ public const int TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC058;
+ public const int TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC059;
+ public const int TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 = 0xC05A;
+ public const int TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 = 0xC05B;
+
+ public const int TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05C;
+ public const int TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05D;
+ public const int TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05E;
+ public const int TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05F;
+ public const int TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC060;
+ public const int TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC061;
+ public const int TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC062;
+ public const int TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC063;
+
+ public const int TLS_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC064;
+ public const int TLS_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC065;
+ public const int TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC066;
+ public const int TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC067;
+ public const int TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC068;
+ public const int TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC069;
+ public const int TLS_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06A;
+ public const int TLS_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06B;
+ public const int TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06C;
+ public const int TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06D;
+ public const int TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06E;
+ public const int TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06F;
+ public const int TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC070;
+ public const int TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC071;
+
+ /*
+ * 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;
+ public const int TLS_RSA_WITH_AES_256_CCM = 0xC09D;
+ public const int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E;
+ public const int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F;
+ public const int TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0;
+ public const int TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1;
+ public const int TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2;
+ public const int TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3;
+ public const int TLS_PSK_WITH_AES_128_CCM = 0xC0A4;
+ public const int TLS_PSK_WITH_AES_256_CCM = 0xC0A5;
+ public const int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6;
+ public const int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7;
+ public const int TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8;
+ public const int TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9;
+ public const int TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA;
+ public const int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB;
+
+ /*
+ * 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;
+
+ /*
+ * RFC 7507
+ */
+ public const int TLS_FALLBACK_SCSV = 0x5600;
+
+ /*
+ * RFC 7905
+ */
+ public const int TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8;
+ public const int TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9;
+ public const int TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA;
+ public const int TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAB;
+ public const int TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC;
+ public const int TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAD;
+ public const int TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAE;
+
+ /*
+ * RFC 8442
+ */
+ public const int TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 = 0xD001;
+ public const int TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 = 0xD002;
+ public const int TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256 = 0xD003;
+ public const int TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 = 0xD005;
+
+ /*
+ * TLS 1.3 Section
+ *
+ * Although TLS 1.3 uses the same cipher suite space as previous versions of TLS, TLS 1.3 cipher
+ * suites are defined differently, only specifying the symmetric ciphers, and cannot be used for
+ * TLS 1.2. Similarly, cipher suites for TLS 1.2 and lower cannot be used with TLS 1.3.
+ */
+
+ /*
+ * RFC 8446
+ */
+ public const int TLS_AES_128_GCM_SHA256 = 0x1301;
+ public const int TLS_AES_256_GCM_SHA384 = 0x1302;
+ public const int TLS_CHACHA20_POLY1305_SHA256 = 0x1303;
+ public const int TLS_AES_128_CCM_SHA256 = 0x1304;
+ public const int TLS_AES_128_CCM_8_SHA256 = 0x1305;
+
+ /*
+ * RFC 8998
+ */
+ public const int TLS_SM4_GCM_SM3 = 0x00C6;
+ public const int TLS_SM4_CCM_SM3 = 0x00C7;
+
+ /*
+ * draft-smyshlyaev-tls12-gost-suites-10
+ */
+ public const int TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC = 0xC100;
+ public const int TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC = 0xC101;
+ public const int TLS_GOSTR341112_256_WITH_28147_CNT_IMIT = 0xC102;
+ }
+}
diff --git a/crypto/src/tls/CipherType.cs b/crypto/src/tls/CipherType.cs
new file mode 100644
index 000000000..3525960ff
--- /dev/null
+++ b/crypto/src/tls/CipherType.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.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>
+ public abstract class CipherType
+ {
+ public const int stream = 0;
+ public const int block = 1;
+
+ /*
+ * RFC 5246
+ */
+ public const int aead = 2;
+ }
+}
diff --git a/crypto/src/tls/ClientAuthenticationType.cs b/crypto/src/tls/ClientAuthenticationType.cs
new file mode 100644
index 000000000..2166e973e
--- /dev/null
+++ b/crypto/src/tls/ClientAuthenticationType.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class ClientAuthenticationType
+ {
+ /*
+ * RFC 5077 4
+ */
+ public const short anonymous = 0;
+ public const short certificate_based = 1;
+ public const short psk = 2;
+ }
+}
diff --git a/crypto/src/tls/ClientCertificateType.cs b/crypto/src/tls/ClientCertificateType.cs
new file mode 100644
index 000000000..f3a4a08a6
--- /dev/null
+++ b/crypto/src/tls/ClientCertificateType.cs
@@ -0,0 +1,69 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class ClientCertificateType
+ {
+ /*
+ * RFC 4346 7.4.4
+ */
+ public const short rsa_sign = 1;
+ public const short dss_sign = 2;
+ public const short rsa_fixed_dh = 3;
+ public const short dss_fixed_dh = 4;
+ public const short rsa_ephemeral_dh_RESERVED = 5;
+ public const short dss_ephemeral_dh_RESERVED = 6;
+ public const short fortezza_dms_RESERVED = 20;
+
+ /*
+ * RFC 4492 5.5
+ */
+ public const short ecdsa_sign = 64;
+ public const short rsa_fixed_ecdh = 65;
+ public const short ecdsa_fixed_ecdh = 66;
+
+ /*
+ * draft-smyshlyaev-tls12-gost-suites-10
+ */
+ public const short gost_sign256 = 67;
+ public const short gost_sign512 = 68;
+
+ public static string GetName(short clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case rsa_sign:
+ return "rsa_sign";
+ case dss_sign:
+ return "dss_sign";
+ case rsa_fixed_dh:
+ return "rsa_fixed_dh";
+ case dss_fixed_dh:
+ return "dss_fixed_dh";
+ case rsa_ephemeral_dh_RESERVED:
+ return "rsa_ephemeral_dh_RESERVED";
+ case dss_ephemeral_dh_RESERVED:
+ return "dss_ephemeral_dh_RESERVED";
+ case fortezza_dms_RESERVED:
+ return "fortezza_dms_RESERVED";
+ case ecdsa_sign:
+ return "ecdsa_sign";
+ case rsa_fixed_ecdh:
+ return "rsa_fixed_ecdh";
+ case ecdsa_fixed_ecdh:
+ return "ecdsa_fixed_ecdh";
+ case gost_sign256:
+ return "gost_sign256";
+ case gost_sign512:
+ return "gost_sign512";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short clientCertificateType)
+ {
+ return GetName(clientCertificateType) + "(" + clientCertificateType + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/ClientHello.cs b/crypto/src/tls/ClientHello.cs
new file mode 100644
index 000000000..50a33ac39
--- /dev/null
+++ b/crypto/src/tls/ClientHello.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class ClientHello
+ {
+ private readonly ProtocolVersion m_version;
+ private readonly byte[] m_random;
+ private readonly byte[] m_sessionID;
+ private readonly byte[] m_cookie;
+ private readonly int[] m_cipherSuites;
+ private readonly IDictionary m_extensions;
+
+ public ClientHello(ProtocolVersion version, byte[] random, byte[] sessionID, byte[] cookie,
+ int[] cipherSuites, IDictionary extensions)
+ {
+ this.m_version = version;
+ this.m_random = random;
+ this.m_sessionID = sessionID;
+ this.m_cookie = cookie;
+ this.m_cipherSuites = cipherSuites;
+ this.m_extensions = extensions;
+ }
+
+ public int[] CipherSuites
+ {
+ get { return m_cipherSuites; }
+ }
+
+ public byte[] Cookie
+ {
+ get { return m_cookie; }
+ }
+
+ public IDictionary Extensions
+ {
+ get { return m_extensions; }
+ }
+
+ public byte[] Random
+ {
+ get { return m_random; }
+ }
+
+ public byte[] SessionID
+ {
+ get { return m_sessionID; }
+ }
+
+ public ProtocolVersion Version
+ {
+ get { return m_version; }
+ }
+
+ /// <summary>Encode this <see cref="ClientHello"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(TlsContext context, Stream output)
+ {
+ TlsUtilities.WriteVersion(m_version, output);
+
+ output.Write(m_random, 0, m_random.Length);
+
+ TlsUtilities.WriteOpaque8(m_sessionID, output);
+
+ if (null != m_cookie)
+ {
+ TlsUtilities.WriteOpaque8(m_cookie, output);
+ }
+
+ TlsUtilities.WriteUint16ArrayWithUint16Length(m_cipherSuites, output);
+
+ TlsUtilities.WriteUint8ArrayWithUint8Length(new short[]{ CompressionMethod.cls_null }, output);
+
+ TlsProtocol.WriteExtensions(output, m_extensions);
+ }
+
+ /// <summary>Parse a <see cref="ClientHello"/> from a <see cref="MemoryStream"/>.</summary>
+ /// <param name="messageInput">the <see cref="MemoryStream"/> to parse from.</param>
+ /// <param name="dtlsOutput">for DTLS this should be non-null; the input is copied to this
+ /// <see cref="Stream"/>, minus the cookie field.</param>
+ /// <returns>a <see cref="ClientHello"/> object.</returns>
+ /// <exception cref="TlsFatalAlert"/>
+ public static ClientHello Parse(MemoryStream messageInput, Stream dtlsOutput)
+ {
+ try
+ {
+ return ImplParse(messageInput, dtlsOutput);
+ }
+ catch (TlsFatalAlert e)
+ {
+ throw e;
+ }
+ catch (IOException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private static ClientHello ImplParse(MemoryStream messageInput, Stream dtlsOutput)
+ {
+ Stream input = messageInput;
+ if (null != dtlsOutput)
+ {
+ input = new TeeInputStream(input, dtlsOutput);
+ }
+
+ ProtocolVersion clientVersion = TlsUtilities.ReadVersion(input);
+
+ byte[] random = TlsUtilities.ReadFully(32, input);
+
+ byte[] sessionID = TlsUtilities.ReadOpaque8(input, 0, 32);
+
+ byte[] cookie = null;
+ if (null != dtlsOutput)
+ {
+ /*
+ * 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.
+ */
+ int maxCookieLength = ProtocolVersion.DTLSv12.IsEqualOrEarlierVersionOf(clientVersion) ? 255 : 32;
+
+ cookie = TlsUtilities.ReadOpaque8(messageInput, 0, maxCookieLength);
+ }
+
+ int cipher_suites_length = TlsUtilities.ReadUint16(input);
+ if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0
+ || (int)(messageInput.Length - messageInput.Position) < cipher_suites_length)
+ {
+ 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."
+ */
+ int[] cipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, input);
+
+ short[] compressionMethods = TlsUtilities.ReadUint8ArrayWithUint8Length(input, 1);
+ if (!Arrays.Contains(compressionMethods, CompressionMethod.cls_null))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ /*
+ * NOTE: Can't use TlsProtocol.ReadExtensions directly because TeeInputStream a) won't have
+ * 'Length' or 'Position' properties in the FIPS provider, b) isn't a MemoryStream.
+ */
+ IDictionary extensions = null;
+ if (messageInput.Position < messageInput.Length)
+ {
+ byte[] extBytes = TlsUtilities.ReadOpaque16(input);
+
+ TlsProtocol.AssertEmpty(messageInput);
+
+ extensions = TlsProtocol.ReadExtensionsDataClientHello(extBytes);
+ }
+
+ return new ClientHello(clientVersion, random, sessionID, cookie, cipherSuites, extensions);
+ }
+ }
+}
diff --git a/crypto/src/tls/CombinedHash.cs b/crypto/src/tls/CombinedHash.cs
new file mode 100644
index 000000000..71151d2a5
--- /dev/null
+++ b/crypto/src/tls/CombinedHash.cs
@@ -0,0 +1,67 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>A combined hash, which implements md5(m) || sha1(m).</summary>
+ public class CombinedHash
+ : TlsHash
+ {
+ protected readonly TlsContext m_context;
+ protected readonly TlsCrypto m_crypto;
+ protected readonly TlsHash m_md5;
+ protected readonly TlsHash m_sha1;
+
+ internal CombinedHash(TlsContext context, TlsHash md5, TlsHash sha1)
+ {
+ this.m_context = context;
+ this.m_crypto = context.Crypto;
+ this.m_md5 = md5;
+ this.m_sha1 = sha1;
+ }
+
+ public CombinedHash(TlsCrypto crypto)
+ {
+ this.m_crypto = crypto;
+ this.m_md5 = crypto.CreateHash(CryptoHashAlgorithm.md5);
+ this.m_sha1 = crypto.CreateHash(CryptoHashAlgorithm.sha1);
+ }
+
+ public CombinedHash(CombinedHash t)
+ {
+ this.m_context = t.m_context;
+ this.m_crypto = t.m_crypto;
+ this.m_md5 = t.m_md5.CloneHash();
+ this.m_sha1 = t.m_sha1.CloneHash();
+ }
+
+ public virtual void Update(byte[] input, int inOff, int len)
+ {
+ m_md5.Update(input, inOff, len);
+ m_sha1.Update(input, inOff, len);
+ }
+
+ public virtual byte[] CalculateHash()
+ {
+ if (null != m_context && TlsUtilities.IsSsl(m_context))
+ {
+ Ssl3Utilities.CompleteCombinedHash(m_context, m_md5, m_sha1);
+ }
+
+ return Arrays.Concatenate(m_md5.CalculateHash(), m_sha1.CalculateHash());
+ }
+
+ public virtual TlsHash CloneHash()
+ {
+ return new CombinedHash(this);
+ }
+
+ public virtual void Reset()
+ {
+ m_md5.Reset();
+ m_sha1.Reset();
+ }
+ }
+}
diff --git a/crypto/src/tls/CompressionMethod.cs b/crypto/src/tls/CompressionMethod.cs
new file mode 100644
index 000000000..21b43d477
--- /dev/null
+++ b/crypto/src/tls/CompressionMethod.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 2246 6.1</summary>
+ public abstract class CompressionMethod
+ {
+ public const short cls_null = 0;
+
+ /*
+ * RFC 3749 2
+ */
+ public const short DEFLATE = 1;
+
+ /*
+ * Values from 224 decimal (0xE0) through 255 decimal (0xFF)
+ * inclusive are reserved for private use.
+ */
+ }
+}
diff --git a/crypto/src/tls/ConnectionEnd.cs b/crypto/src/tls/ConnectionEnd.cs
new file mode 100644
index 000000000..01ceb1b25
--- /dev/null
+++ b/crypto/src/tls/ConnectionEnd.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.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>
+ public abstract class ConnectionEnd
+ {
+ public const int server = 0;
+ public const int client = 1;
+ }
+}
diff --git a/crypto/src/tls/ContentType.cs b/crypto/src/tls/ContentType.cs
new file mode 100644
index 000000000..cf89539c7
--- /dev/null
+++ b/crypto/src/tls/ContentType.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 2246 6.2.1</summary>
+ public abstract class ContentType
+ {
+ public const short change_cipher_spec = 20;
+ public const short alert = 21;
+ public const short handshake = 22;
+ public const short application_data = 23;
+ public const short heartbeat = 24;
+
+ public static string GetName(short contentType)
+ {
+ switch (contentType)
+ {
+ case alert:
+ return "alert";
+ case application_data:
+ return "application_data";
+ case change_cipher_spec:
+ return "change_cipher_spec";
+ case handshake:
+ return "handshake";
+ case heartbeat:
+ return "heartbeat";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short contentType)
+ {
+ return GetName(contentType) + "(" + contentType + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/DatagramReceiver.cs b/crypto/src/tls/DatagramReceiver.cs
new file mode 100644
index 000000000..5ab605ac4
--- /dev/null
+++ b/crypto/src/tls/DatagramReceiver.cs
@@ -0,0 +1,14 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public interface DatagramReceiver
+ {
+ /// <exception cref="IOException"/>
+ int GetReceiveLimit();
+
+ /// <exception cref="IOException"/>
+ int Receive(byte[] buf, int off, int len, int waitMillis);
+ }
+}
diff --git a/crypto/src/tls/DatagramSender.cs b/crypto/src/tls/DatagramSender.cs
new file mode 100644
index 000000000..bf14c18fe
--- /dev/null
+++ b/crypto/src/tls/DatagramSender.cs
@@ -0,0 +1,14 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public interface DatagramSender
+ {
+ /// <exception cref="IOException"/>
+ int GetSendLimit();
+
+ /// <exception cref="IOException"/>
+ void Send(byte[] buf, int off, int len);
+ }
+}
diff --git a/crypto/src/tls/DatagramTransport.cs b/crypto/src/tls/DatagramTransport.cs
new file mode 100644
index 000000000..4aecd0515
--- /dev/null
+++ b/crypto/src/tls/DatagramTransport.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for an object sending and receiving DTLS data.</summary>
+ public interface DatagramTransport
+ : DatagramReceiver, DatagramSender, TlsCloseable
+ {
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsClient.cs b/crypto/src/tls/DefaultTlsClient.cs
new file mode 100644
index 000000000..a2a742633
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsClient.cs
@@ -0,0 +1,48 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class DefaultTlsClient
+ : AbstractTlsClient
+ {
+ private static readonly int[] DefaultCipherSuites = new int[]
+ {
+ /*
+ * TODO[tls13] TLS 1.3
+ */
+ //CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
+ //CipherSuite.TLS_AES_128_GCM_SHA256,
+
+ /*
+ * pre-TLS 1.3
+ */
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ 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_CHACHA20_POLY1305_SHA256,
+ 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_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ 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,
+ };
+
+ public DefaultTlsClient(TlsCrypto crypto)
+ : base(crypto)
+ {
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
+ }
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsCredentialedSigner.cs b/crypto/src/tls/DefaultTlsCredentialedSigner.cs
new file mode 100644
index 000000000..64bc30a8e
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsCredentialedSigner.cs
@@ -0,0 +1,66 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Container class for generating signatures that carries the signature type, parameters, public key
+ /// certificate and public key's associated signer object.</summary>
+ public class DefaultTlsCredentialedSigner
+ : TlsCredentialedSigner
+ {
+ protected readonly TlsCryptoParameters m_cryptoParams;
+ protected readonly Certificate m_certificate;
+ protected readonly SignatureAndHashAlgorithm m_signatureAndHashAlgorithm;
+ protected readonly TlsSigner m_signer;
+
+ public DefaultTlsCredentialedSigner(TlsCryptoParameters cryptoParams, TlsSigner signer,
+ Certificate certificate, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ if (certificate == null)
+ throw new ArgumentNullException("certificate");
+ if (certificate.IsEmpty)
+ throw new ArgumentException("cannot be empty", "certificate");
+ if (signer == null)
+ throw new ArgumentNullException("signer");
+
+ this.m_cryptoParams = cryptoParams;
+ this.m_certificate = certificate;
+ this.m_signatureAndHashAlgorithm = signatureAndHashAlgorithm;
+ this.m_signer = signer;
+ }
+
+ public virtual Certificate Certificate
+ {
+ get { return m_certificate; }
+ }
+
+ public virtual byte[] GenerateRawSignature(byte[] hash)
+ {
+ return m_signer.GenerateRawSignature(GetEffectiveAlgorithm(), hash);
+ }
+
+ public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+ {
+ get { return m_signatureAndHashAlgorithm; }
+ }
+
+ public virtual TlsStreamSigner GetStreamSigner()
+ {
+ return m_signer.GetStreamSigner(GetEffectiveAlgorithm());
+ }
+
+ protected virtual SignatureAndHashAlgorithm GetEffectiveAlgorithm()
+ {
+ SignatureAndHashAlgorithm algorithm = null;
+ if (TlsImplUtilities.IsTlsV12(m_cryptoParams))
+ {
+ algorithm = SignatureAndHashAlgorithm;
+ if (algorithm == null)
+ throw new InvalidOperationException("'signatureAndHashAlgorithm' cannot be null for (D)TLS 1.2+");
+ }
+ return algorithm;
+ }
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsDHGroupVerifier.cs b/crypto/src/tls/DefaultTlsDHGroupVerifier.cs
new file mode 100644
index 000000000..8b9cf2e0f
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsDHGroupVerifier.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DefaultTlsDHGroupVerifier
+ : TlsDHGroupVerifier
+ {
+ public static readonly int DefaultMinimumPrimeBits = 2048;
+
+ private static readonly IList DefaultGroups = Platform.CreateArrayList();
+
+ private static void AddDefaultGroup(DHGroup dhGroup)
+ {
+ DefaultGroups.Add(dhGroup);
+ }
+
+ static DefaultTlsDHGroupVerifier()
+ {
+ /*
+ * These 10 standard groups are those specified in NIST SP 800-56A Rev. 3 Appendix D. Make
+ * sure to consider the impact on BCJSSE's FIPS mode and/or usage with the BCFIPS provider
+ * before modifying this list.
+ */
+
+ AddDefaultGroup(DHStandardGroups.rfc3526_2048);
+ AddDefaultGroup(DHStandardGroups.rfc3526_3072);
+ AddDefaultGroup(DHStandardGroups.rfc3526_4096);
+ AddDefaultGroup(DHStandardGroups.rfc3526_6144);
+ AddDefaultGroup(DHStandardGroups.rfc3526_8192);
+
+ AddDefaultGroup(DHStandardGroups.rfc7919_ffdhe2048);
+ AddDefaultGroup(DHStandardGroups.rfc7919_ffdhe3072);
+ AddDefaultGroup(DHStandardGroups.rfc7919_ffdhe4096);
+ AddDefaultGroup(DHStandardGroups.rfc7919_ffdhe6144);
+ AddDefaultGroup(DHStandardGroups.rfc7919_ffdhe8192);
+ }
+
+ // IList is (DHGroup)
+ protected readonly IList m_groups;
+ protected readonly int m_minimumPrimeBits;
+
+ /// <summary>Accept named groups and various standard DH groups with 'P' at least
+ /// <see cref="DefaultMinimumPrimeBits"/> bits.</summary>
+ public DefaultTlsDHGroupVerifier()
+ : this(DefaultMinimumPrimeBits)
+ {
+ }
+
+ /// <summary>Accept named groups and various standard DH groups with 'P' at least the specified number of bits.
+ /// </summary>
+ /// <param name="minimumPrimeBits">the minimum bitlength of 'P'.</param>
+ public DefaultTlsDHGroupVerifier(int minimumPrimeBits)
+ : this(DefaultGroups, minimumPrimeBits)
+ {
+ }
+
+ /// <summary>Accept named groups and a custom set of group parameters, subject to a minimum bitlength for 'P'.
+ /// </summary>
+ /// <param name="groups">a <see cref="IList">list</see> of acceptable <see cref="DHGroup"/>s.</param>
+ /// <param name="minimumPrimeBits">the minimum bitlength of 'P'.</param>
+ public DefaultTlsDHGroupVerifier(IList groups, int minimumPrimeBits)
+ {
+ this.m_groups = Platform.CreateArrayList(groups);
+ this.m_minimumPrimeBits = minimumPrimeBits;
+ }
+
+ public virtual bool Accept(DHGroup dhGroup)
+ {
+ return CheckMinimumPrimeBits(dhGroup) && CheckGroup(dhGroup);
+ }
+
+ public virtual int MinimumPrimeBits
+ {
+ get { return m_minimumPrimeBits; }
+ }
+
+ protected virtual bool AreGroupsEqual(DHGroup a, DHGroup b)
+ {
+ return a == b || (AreParametersEqual(a.P, b.P) && AreParametersEqual(a.G, b.G));
+ }
+
+ protected virtual bool AreParametersEqual(BigInteger a, BigInteger b)
+ {
+ return a == b || a.Equals(b);
+ }
+
+ protected virtual bool CheckGroup(DHGroup dhGroup)
+ {
+ foreach (DHGroup group in m_groups)
+ {
+ if (AreGroupsEqual(dhGroup, group))
+ return true;
+ }
+ return false;
+ }
+
+ protected virtual bool CheckMinimumPrimeBits(DHGroup dhGroup)
+ {
+ return dhGroup.P.BitLength >= MinimumPrimeBits;
+ }
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsHeartbeat.cs b/crypto/src/tls/DefaultTlsHeartbeat.cs
new file mode 100644
index 000000000..9090d2cc0
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsHeartbeat.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DefaultTlsHeartbeat
+ : TlsHeartbeat
+ {
+ private readonly int idleMillis, timeoutMillis;
+
+ private uint counter = 0U;
+
+ public DefaultTlsHeartbeat(int idleMillis, int timeoutMillis)
+ {
+ if (idleMillis <= 0)
+ throw new ArgumentException("must be > 0", "idleMillis");
+ if (timeoutMillis <= 0)
+ throw new ArgumentException("must be > 0", "timeoutMillis");
+
+ this.idleMillis = idleMillis;
+ this.timeoutMillis = timeoutMillis;
+ }
+
+ public virtual byte[] GeneratePayload()
+ {
+ lock (this)
+ {
+ // NOTE: The counter naturally wraps back to 0
+ return Pack.UInt32_To_BE(++counter);
+ }
+ }
+
+ public virtual int IdleMillis
+ {
+ get { return idleMillis; }
+ }
+
+ public virtual int TimeoutMillis
+ {
+ get { return timeoutMillis; }
+ }
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsKeyExchangeFactory.cs b/crypto/src/tls/DefaultTlsKeyExchangeFactory.cs
new file mode 100644
index 000000000..c8d6ff130
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsKeyExchangeFactory.cs
@@ -0,0 +1,89 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DefaultTlsKeyExchangeFactory
+ : AbstractTlsKeyExchangeFactory
+ {
+ public override TlsKeyExchange CreateDHKeyExchange(int keyExchange)
+ {
+ return new TlsDHKeyExchange(keyExchange);
+ }
+
+ public override TlsKeyExchange CreateDHanonKeyExchangeClient(int keyExchange,
+ TlsDHGroupVerifier dhGroupVerifier)
+ {
+ return new TlsDHanonKeyExchange(keyExchange, dhGroupVerifier);
+ }
+
+ public override TlsKeyExchange CreateDHanonKeyExchangeServer(int keyExchange, TlsDHConfig dhConfig)
+ {
+ return new TlsDHanonKeyExchange(keyExchange, dhConfig);
+ }
+
+ public override TlsKeyExchange CreateDheKeyExchangeClient(int keyExchange, TlsDHGroupVerifier dhGroupVerifier)
+ {
+ return new TlsDheKeyExchange(keyExchange, dhGroupVerifier);
+ }
+
+ public override TlsKeyExchange CreateDheKeyExchangeServer(int keyExchange, TlsDHConfig dhConfig)
+ {
+ return new TlsDheKeyExchange(keyExchange, dhConfig);
+ }
+
+ public override TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
+ {
+ return new TlsECDHKeyExchange(keyExchange);
+ }
+
+ public override TlsKeyExchange CreateECDHanonKeyExchangeClient(int keyExchange)
+ {
+ return new TlsECDHanonKeyExchange(keyExchange);
+ }
+
+ public override TlsKeyExchange CreateECDHanonKeyExchangeServer(int keyExchange, TlsECConfig ecConfig)
+ {
+ return new TlsECDHanonKeyExchange(keyExchange, ecConfig);
+ }
+
+ public override TlsKeyExchange CreateECDheKeyExchangeClient(int keyExchange)
+ {
+ return new TlsECDheKeyExchange(keyExchange);
+ }
+
+ public override TlsKeyExchange CreateECDheKeyExchangeServer(int keyExchange, TlsECConfig ecConfig)
+ {
+ return new TlsECDheKeyExchange(keyExchange, ecConfig);
+ }
+
+ public override TlsKeyExchange CreatePskKeyExchangeClient(int keyExchange, TlsPskIdentity pskIdentity,
+ TlsDHGroupVerifier dhGroupVerifier)
+ {
+ return new TlsPskKeyExchange(keyExchange, pskIdentity, dhGroupVerifier);
+ }
+
+ public override TlsKeyExchange CreatePskKeyExchangeServer(int keyExchange,
+ TlsPskIdentityManager pskIdentityManager, TlsDHConfig dhConfig, TlsECConfig ecConfig)
+ {
+ return new TlsPskKeyExchange(keyExchange, pskIdentityManager, dhConfig, ecConfig);
+ }
+
+ public override TlsKeyExchange CreateRsaKeyExchange(int keyExchange)
+ {
+ return new TlsRsaKeyExchange(keyExchange);
+ }
+
+ public override TlsKeyExchange CreateSrpKeyExchangeClient(int keyExchange, TlsSrpIdentity srpIdentity,
+ TlsSrpConfigVerifier srpConfigVerifier)
+ {
+ return new TlsSrpKeyExchange(keyExchange, srpIdentity, srpConfigVerifier);
+ }
+
+ public override TlsKeyExchange CreateSrpKeyExchangeServer(int keyExchange, TlsSrpLoginParameters loginParameters)
+ {
+ return new TlsSrpKeyExchange(keyExchange, loginParameters);
+ }
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsServer.cs b/crypto/src/tls/DefaultTlsServer.cs
new file mode 100644
index 000000000..de8a3f4a0
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsServer.cs
@@ -0,0 +1,108 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class DefaultTlsServer
+ : AbstractTlsServer
+ {
+ private static readonly int[] DefaultCipherSuites = new int[]
+ {
+ /*
+ * TODO[tls13] TLS 1.3
+ */
+ //CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
+ //CipherSuite.TLS_AES_256_GCM_SHA384,
+ //CipherSuite.TLS_AES_128_GCM_SHA256,
+
+ /*
+ * pre-TLS 1.3
+ */
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ 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_CHACHA20_POLY1305_SHA256,
+ 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 DefaultTlsServer(TlsCrypto crypto)
+ : base(crypto)
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedSigner GetDsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedSigner GetECDsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ int keyExchangeAlgorithm = m_context.SecurityParameters.KeyExchangeAlgorithm;
+
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DHE_DSS:
+ return GetDsaSignerCredentials();
+
+ case KeyExchangeAlgorithm.DH_anon:
+ case KeyExchangeAlgorithm.ECDH_anon:
+ return null;
+
+ 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);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/DefaultTlsSrpConfigVerifier.cs b/crypto/src/tls/DefaultTlsSrpConfigVerifier.cs
new file mode 100644
index 000000000..781249829
--- /dev/null
+++ b/crypto/src/tls/DefaultTlsSrpConfigVerifier.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DefaultTlsSrpConfigVerifier
+ : TlsSrpConfigVerifier
+ {
+ private static readonly IList DefaultGroups = Platform.CreateArrayList();
+
+ static DefaultTlsSrpConfigVerifier()
+ {
+ 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);
+ }
+
+ // IList is (SRP6Group)
+ protected readonly IList m_groups;
+
+ /// <summary>Accept only the group parameters specified in RFC 5054 Appendix A.</summary>
+ public DefaultTlsSrpConfigVerifier()
+ : this(DefaultGroups)
+ {
+ }
+
+ /// <summary>Specify a custom set of acceptable group parameters.</summary>
+ /// <param name="groups">an <see cref="IList"/> of acceptable <see cref="Srp6Group"/>.</param>
+ public DefaultTlsSrpConfigVerifier(IList groups)
+ {
+ this.m_groups = Platform.CreateArrayList(groups);
+ }
+
+ public virtual bool Accept(TlsSrpConfig srpConfig)
+ {
+ foreach (Srp6Group group in m_groups)
+ {
+ if (AreGroupsEqual(srpConfig, group))
+ return true;
+ }
+ return false;
+ }
+
+ protected virtual bool AreGroupsEqual(TlsSrpConfig a, Srp6Group b)
+ {
+ BigInteger[] ng = a.GetExplicitNG();
+ return AreParametersEqual(ng[0], b.N) && AreParametersEqual(ng[1], b.G);
+ }
+
+ protected virtual bool AreParametersEqual(BigInteger a, BigInteger b)
+ {
+ return a == b || a.Equals(b);
+ }
+ }
+}
diff --git a/crypto/src/tls/DeferredHash.cs b/crypto/src/tls/DeferredHash.cs
new file mode 100644
index 000000000..43d60d07c
--- /dev/null
+++ b/crypto/src/tls/DeferredHash.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Buffers input until the hash algorithm is determined.</summary>
+ internal sealed class DeferredHash
+ : TlsHandshakeHash
+ {
+ private const int BufferingHashLimit = 4;
+
+ private readonly TlsContext m_context;
+
+ private DigestInputBuffer m_buf;
+ private readonly IDictionary m_hashes;
+ private bool m_forceBuffering;
+ private bool m_sealed;
+
+ internal DeferredHash(TlsContext context)
+ {
+ this.m_context = context;
+ this.m_buf = new DigestInputBuffer();
+ this.m_hashes = Platform.CreateHashtable();
+ this.m_forceBuffering = false;
+ this.m_sealed = false;
+ }
+
+ private DeferredHash(TlsContext context, IDictionary hashes)
+ {
+ this.m_context = context;
+ this.m_buf = null;
+ this.m_hashes = hashes;
+ this.m_forceBuffering = false;
+ this.m_sealed = true;
+ }
+
+ /// <exception cref="IOException"/>
+ public void CopyBufferTo(Stream output)
+ {
+ if (m_buf == null)
+ {
+ // If you see this, you need to call forceBuffering() before SealHashAlgorithms()
+ throw new InvalidOperationException("Not buffering");
+ }
+
+ m_buf.CopyTo(output);
+ }
+
+ public void ForceBuffering()
+ {
+ if (m_sealed)
+ throw new InvalidOperationException("Too late to force buffering");
+
+ this.m_forceBuffering = true;
+ }
+
+ public void NotifyPrfDetermined()
+ {
+ SecurityParameters securityParameters = m_context.SecurityParameters;
+
+ switch (securityParameters.PrfAlgorithm)
+ {
+ case PrfAlgorithm.ssl_prf_legacy:
+ case PrfAlgorithm.tls_prf_legacy:
+ {
+ CheckTrackingHash(CryptoHashAlgorithm.md5);
+ CheckTrackingHash(CryptoHashAlgorithm.sha1);
+ break;
+ }
+ default:
+ {
+ CheckTrackingHash(securityParameters.PrfHashAlgorithm);
+ if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
+ {
+ SealHashAlgorithms();
+ }
+ break;
+ }
+ }
+ }
+
+ public void TrackHashAlgorithm(int cryptoHashAlgorithm)
+ {
+ if (m_sealed)
+ throw new InvalidOperationException("Too late to track more hash algorithms");
+
+ CheckTrackingHash(cryptoHashAlgorithm);
+ }
+
+ public void SealHashAlgorithms()
+ {
+ if (m_sealed)
+ throw new InvalidOperationException("Already sealed");
+
+ this.m_sealed = true;
+ CheckStopBuffering();
+ }
+
+ public TlsHandshakeHash StopTracking()
+ {
+ SecurityParameters securityParameters = m_context.SecurityParameters;
+
+ IDictionary newHashes = Platform.CreateHashtable();
+ switch (securityParameters.PrfAlgorithm)
+ {
+ case PrfAlgorithm.ssl_prf_legacy:
+ case PrfAlgorithm.tls_prf_legacy:
+ {
+ CloneHash(newHashes, HashAlgorithm.md5);
+ CloneHash(newHashes, HashAlgorithm.sha1);
+ break;
+ }
+ default:
+ {
+ CloneHash(newHashes, securityParameters.PrfHashAlgorithm);
+ break;
+ }
+ }
+ return new DeferredHash(m_context, newHashes);
+ }
+
+ public TlsHash ForkPrfHash()
+ {
+ CheckStopBuffering();
+
+ SecurityParameters securityParameters = m_context.SecurityParameters;
+
+ TlsHash prfHash;
+ switch (securityParameters.PrfAlgorithm)
+ {
+ case PrfAlgorithm.ssl_prf_legacy:
+ case PrfAlgorithm.tls_prf_legacy:
+ {
+ prfHash = new CombinedHash(m_context, CloneHash(HashAlgorithm.md5), CloneHash(HashAlgorithm.sha1));
+ break;
+ }
+ default:
+ {
+ prfHash = CloneHash(securityParameters.PrfHashAlgorithm);
+ break;
+ }
+ }
+
+ if (m_buf != null)
+ {
+ m_buf.UpdateDigest(prfHash);
+ }
+
+ return prfHash;
+ }
+
+ public byte[] GetFinalHash(int cryptoHashAlgorithm)
+ {
+ TlsHash d = (TlsHash)m_hashes[cryptoHashAlgorithm];
+ if (d == null)
+ throw new InvalidOperationException("CryptoHashAlgorithm." + cryptoHashAlgorithm
+ + " is not being tracked");
+
+ CheckStopBuffering();
+
+ d = d.CloneHash();
+ if (m_buf != null)
+ {
+ m_buf.UpdateDigest(d);
+ }
+
+ return d.CalculateHash();
+ }
+
+ public void Update(byte[] input, int inOff, int len)
+ {
+ if (m_buf != null)
+ {
+ m_buf.Write(input, inOff, len);
+ return;
+ }
+
+ foreach (TlsHash hash in m_hashes.Values)
+ {
+ hash.Update(input, inOff, len);
+ }
+ }
+
+ public byte[] CalculateHash()
+ {
+ throw new InvalidOperationException("Use 'ForkPrfHash' to get a definite hash");
+ }
+
+ public TlsHash CloneHash()
+ {
+ throw new InvalidOperationException("attempt to clone a DeferredHash");
+ }
+
+ public void Reset()
+ {
+ if (m_buf != null)
+ {
+ m_buf.SetLength(0);
+ return;
+ }
+
+ foreach (TlsHash hash in m_hashes.Values)
+ {
+ hash.Reset();
+ }
+ }
+
+ private void CheckStopBuffering()
+ {
+ if (!m_forceBuffering && m_sealed && m_buf != null && m_hashes.Count <= BufferingHashLimit)
+ {
+ foreach (TlsHash hash in m_hashes.Values)
+ {
+ m_buf.UpdateDigest(hash);
+ }
+
+ this.m_buf = null;
+ }
+ }
+
+ private void CheckTrackingHash(int cryptoHashAlgorithm)
+ {
+ if (!m_hashes.Contains(cryptoHashAlgorithm))
+ {
+ TlsHash hash = m_context.Crypto.CreateHash(cryptoHashAlgorithm);
+ m_hashes[cryptoHashAlgorithm] = hash;
+ }
+ }
+
+ private TlsHash CloneHash(int cryptoHashAlgorithm)
+ {
+ return ((TlsHash)m_hashes[cryptoHashAlgorithm]).CloneHash();
+ }
+
+ private void CloneHash(IDictionary newHashes, int cryptoHashAlgorithm)
+ {
+ TlsHash hash = CloneHash(cryptoHashAlgorithm);
+ if (m_buf != null)
+ {
+ m_buf.UpdateDigest(hash);
+ }
+ newHashes[cryptoHashAlgorithm] = hash;
+ }
+ }
+}
diff --git a/crypto/src/tls/DigestInputBuffer.cs b/crypto/src/tls/DigestInputBuffer.cs
new file mode 100644
index 000000000..7dd525f88
--- /dev/null
+++ b/crypto/src/tls/DigestInputBuffer.cs
@@ -0,0 +1,26 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class DigestInputBuffer
+ : MemoryStream
+ {
+ internal void UpdateDigest(TlsHash hash)
+ {
+ Streams.WriteBufTo(this, new TlsHashSink(hash));
+ }
+
+ /// <exception cref="IOException"/>
+ internal void CopyTo(Stream output)
+ {
+ // TODO[tls-port]
+ // NOTE: Copy data since the output here may be under control of external code.
+ //Streams.PipeAll(new MemoryStream(buf, 0, count), output);
+ Streams.WriteBufTo(this, output);
+ }
+ }
+}
diff --git a/crypto/src/tls/DigitallySigned.cs b/crypto/src/tls/DigitallySigned.cs
new file mode 100644
index 000000000..e977b350b
--- /dev/null
+++ b/crypto/src/tls/DigitallySigned.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class DigitallySigned
+ {
+ private readonly SignatureAndHashAlgorithm algorithm;
+ private readonly byte[] signature;
+
+ public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature)
+ {
+ if (signature == null)
+ throw new ArgumentNullException("signature");
+
+ this.algorithm = algorithm;
+ this.signature = signature;
+ }
+
+ /// <returns>a <see cref="SignatureAndHashAlgorithm"/> (or null before TLS 1.2).</returns>
+ public SignatureAndHashAlgorithm Algorithm
+ {
+ get { return algorithm; }
+ }
+
+ public byte[] Signature
+ {
+ get { return signature; }
+ }
+
+ /// <summary>Encode this <see cref="DigitallySigned"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ if (algorithm != null)
+ {
+ algorithm.Encode(output);
+ }
+ TlsUtilities.WriteOpaque16(signature, output);
+ }
+
+ /// <summary>Parse a <see cref="DigitallySigned"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="DigitallySigned"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static DigitallySigned Parse(TlsContext context, Stream input)
+ {
+ SignatureAndHashAlgorithm algorithm = null;
+ if (TlsUtilities.IsTlsV12(context))
+ {
+ algorithm = SignatureAndHashAlgorithm.Parse(input);
+
+ if (SignatureAlgorithm.anonymous == algorithm.Signature)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ byte[] signature = TlsUtilities.ReadOpaque16(input);
+ return new DigitallySigned(algorithm, signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs
new file mode 100644
index 000000000..dea35a28b
--- /dev/null
+++ b/crypto/src/tls/DtlsClientProtocol.cs
@@ -0,0 +1,976 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DtlsClientProtocol
+ : DtlsProtocol
+ {
+ public DtlsClientProtocol()
+ : base()
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual DtlsTransport Connect(TlsClient client, DatagramTransport transport)
+ {
+ if (client == null)
+ throw new ArgumentNullException("client");
+ if (transport == null)
+ throw new ArgumentNullException("transport");
+
+ ClientHandshakeState state = new ClientHandshakeState();
+ state.client = client;
+ state.clientContext = new TlsClientContextImpl(client.Crypto);
+
+ client.Init(state.clientContext);
+ state.clientContext.HandshakeBeginning(client);
+
+ SecurityParameters securityParameters = state.clientContext.SecurityParameters;
+ securityParameters.m_extendedPadding = client.ShouldUseExtendedPadding();
+
+ TlsSession sessionToResume = state.client.GetSessionToResume();
+ if (sessionToResume != null && sessionToResume.IsResumable)
+ {
+ SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
+
+ /*
+ * NOTE: If we ever enable session resumption without extended_master_secret, then
+ * renegotiation MUST be disabled (see RFC 7627 5.4).
+ */
+ if (sessionParameters != null
+ && (sessionParameters.IsExtendedMasterSecret
+ || (!state.client.RequiresExtendedMasterSecret() && state.client.AllowLegacyResumption())))
+ {
+ TlsSecret masterSecret = sessionParameters.MasterSecret;
+ lock (masterSecret)
+ {
+ if (masterSecret.IsAlive())
+ {
+ state.tlsSession = sessionToResume;
+ state.sessionParameters = sessionParameters;
+ state.sessionMasterSecret = state.clientContext.Crypto.AdoptSecret(masterSecret);
+ }
+ }
+ }
+ }
+
+ DtlsRecordLayer recordLayer = new DtlsRecordLayer(state.clientContext, state.client, transport);
+ client.NotifyCloseHandle(recordLayer);
+
+ try
+ {
+ return ClientHandshake(state, recordLayer);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ AbortClientHandshake(state, recordLayer, fatalAlert.AlertDescription);
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ AbortClientHandshake(state, recordLayer, AlertDescription.internal_error);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ AbortClientHandshake(state, recordLayer, AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ finally
+ {
+ securityParameters.Clear();
+ }
+ }
+
+ internal virtual void AbortClientHandshake(ClientHandshakeState state, DtlsRecordLayer recordLayer,
+ short alertDescription)
+ {
+ recordLayer.Fail(alertDescription);
+ InvalidateSession(state);
+ }
+
+ /// <exception cref="IOException"/>
+ internal virtual DtlsTransport ClientHandshake(ClientHandshakeState state, DtlsRecordLayer recordLayer)
+ {
+ SecurityParameters securityParameters = state.clientContext.SecurityParameters;
+
+ DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.clientContext, recordLayer,
+ state.client.GetHandshakeTimeoutMillis(), null);
+
+ byte[] clientHelloBody = GenerateClientHello(state);
+
+ recordLayer.SetWriteVersion(ProtocolVersion.DTLSv10);
+
+ handshake.SendMessage(HandshakeType.client_hello, clientHelloBody);
+
+ DtlsReliableHandshake.Message serverMessage = handshake.ReceiveMessage();
+
+ // TODO Consider stricter HelloVerifyRequest protocol
+ //if (serverMessage.Type == HandshakeType.hello_verify_request)
+ while (serverMessage.Type == HandshakeType.hello_verify_request)
+ {
+ byte[] cookie = ProcessHelloVerifyRequest(state, serverMessage.Body);
+ byte[] patched = PatchClientHelloWithCookie(clientHelloBody, cookie);
+
+ handshake.ResetAfterHelloVerifyRequestClient();
+ handshake.SendMessage(HandshakeType.client_hello, patched);
+
+ serverMessage = handshake.ReceiveMessage();
+ }
+
+ if (serverMessage.Type == HandshakeType.server_hello)
+ {
+ ProtocolVersion recordLayerVersion = recordLayer.ReadVersion;
+ ReportServerVersion(state, recordLayerVersion);
+ recordLayer.SetWriteVersion(recordLayerVersion);
+
+ ProcessServerHello(state, serverMessage.Body);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ handshake.HandshakeHash.NotifyPrfDetermined();
+
+ ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
+
+ if (state.resumedSession)
+ {
+ securityParameters.m_masterSecret = state.sessionMasterSecret;
+ recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(state.clientContext));
+
+ // NOTE: Calculated exclusive of the actual Finished message from the server
+ securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+ handshake.HandshakeHash, true);
+ ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished),
+ securityParameters.PeerVerifyData);
+
+ // NOTE: Calculated exclusive of the Finished message itself
+ securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+ handshake.HandshakeHash, false);
+ handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
+
+ handshake.Finish();
+
+ if (securityParameters.IsExtendedMasterSecret)
+ {
+ securityParameters.m_tlsUnique = securityParameters.PeerVerifyData;
+ }
+
+ securityParameters.m_localCertificate = state.sessionParameters.LocalCertificate;
+ securityParameters.m_peerCertificate = state.sessionParameters.PeerCertificate;
+ securityParameters.m_pskIdentity = state.sessionParameters.PskIdentity;
+ securityParameters.m_srpIdentity = state.sessionParameters.SrpIdentity;
+
+ state.clientContext.HandshakeComplete(state.client, state.tlsSession);
+
+ recordLayer.InitHeartbeat(state.heartbeat,
+ HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
+
+ return new DtlsTransport(recordLayer);
+ }
+
+ InvalidateSession(state);
+
+ state.tlsSession = TlsUtilities.ImportSession(securityParameters.SessionID, null);
+ state.sessionParameters = null;
+ state.sessionMasterSecret = null;
+
+ serverMessage = handshake.ReceiveMessage();
+
+ if (serverMessage.Type == HandshakeType.supplemental_data)
+ {
+ ProcessServerSupplementalData(state, serverMessage.Body);
+ serverMessage = handshake.ReceiveMessage();
+ }
+ else
+ {
+ state.client.ProcessServerSupplementalData(null);
+ }
+
+ state.keyExchange = TlsUtilities.InitKeyExchangeClient(state.clientContext, state.client);
+
+ if (serverMessage.Type == HandshakeType.certificate)
+ {
+ ProcessServerCertificate(state, serverMessage.Body);
+ serverMessage = handshake.ReceiveMessage();
+ }
+ else
+ {
+ // Okay, Certificate is optional
+ state.authentication = null;
+ }
+
+ if (serverMessage.Type == HandshakeType.certificate_status)
+ {
+ if (securityParameters.StatusRequestVersion < 1)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ProcessCertificateStatus(state, serverMessage.Body);
+ serverMessage = handshake.ReceiveMessage();
+ }
+ else
+ {
+ // Okay, CertificateStatus is optional
+ }
+
+ TlsUtilities.ProcessServerCertificate(state.clientContext, state.certificateStatus, state.keyExchange,
+ state.authentication, state.clientExtensions, state.serverExtensions);
+
+ 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);
+
+ TlsUtilities.EstablishServerSigAlgs(securityParameters, state.certificateRequest);
+
+ /*
+ * 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, securityParameters.ServerSigAlgs);
+
+ 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);
+ }
+
+ IList clientSupplementalData = state.client.GetClientSupplementalData();
+ if (clientSupplementalData != null)
+ {
+ byte[] supplementalDataBody = GenerateSupplementalData(clientSupplementalData);
+ handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody);
+ }
+
+ if (null != state.certificateRequest)
+ {
+ state.clientCredentials = TlsUtilities.EstablishClientCredentials(state.authentication,
+ 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 (null != state.clientCredentials)
+ {
+ clientCertificate = state.clientCredentials.Certificate;
+ }
+
+ SendCertificateMessage(state.clientContext, handshake, clientCertificate, null);
+ }
+
+ TlsCredentialedSigner credentialedSigner = null;
+ TlsStreamSigner streamSigner = null;
+
+ if (null != state.clientCredentials)
+ {
+ state.keyExchange.ProcessClientCredentials(state.clientCredentials);
+
+ if (state.clientCredentials is TlsCredentialedSigner)
+ {
+ credentialedSigner = (TlsCredentialedSigner)state.clientCredentials;
+ streamSigner = credentialedSigner.GetStreamSigner();
+ }
+ }
+ else
+ {
+ state.keyExchange.SkipClientCredentials();
+ }
+
+ bool forceBuffering = streamSigner != null;
+ TlsUtilities.SealHandshakeHash(state.clientContext, handshake.HandshakeHash, forceBuffering);
+
+ byte[] clientKeyExchangeBody = GenerateClientKeyExchange(state);
+ handshake.SendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody);
+
+ securityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(handshake.HandshakeHash);
+
+ TlsProtocol.EstablishMasterSecret(state.clientContext, state.keyExchange);
+ recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(state.clientContext));
+
+ {
+ if (credentialedSigner != null)
+ {
+ DigitallySigned certificateVerify = TlsUtilities.GenerateCertificateVerifyClient(
+ state.clientContext, credentialedSigner, streamSigner, handshake.HandshakeHash);
+ byte[] certificateVerifyBody = GenerateCertificateVerify(state, certificateVerify);
+ handshake.SendMessage(HandshakeType.certificate_verify, certificateVerifyBody);
+ }
+
+ handshake.PrepareToFinish();
+ }
+
+ securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+ handshake.HandshakeHash, false);
+ handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
+
+ if (state.expectSessionTicket)
+ {
+ serverMessage = handshake.ReceiveMessage();
+ if (serverMessage.Type == HandshakeType.new_session_ticket)
+ {
+ ProcessNewSessionTicket(state, serverMessage.Body);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ // NOTE: Calculated exclusive of the actual Finished message from the server
+ securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+ handshake.HandshakeHash, true);
+ ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), securityParameters.PeerVerifyData);
+
+ handshake.Finish();
+
+ state.sessionMasterSecret = securityParameters.MasterSecret;
+
+ state.sessionParameters = new SessionParameters.Builder()
+ .SetCipherSuite(securityParameters.CipherSuite)
+ .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
+ .SetLocalCertificate(securityParameters.LocalCertificate)
+ .SetMasterSecret(state.clientContext.Crypto.AdoptSecret(state.sessionMasterSecret))
+ .SetNegotiatedVersion(securityParameters.NegotiatedVersion)
+ .SetPeerCertificate(securityParameters.PeerCertificate)
+ .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);
+
+ securityParameters.m_tlsUnique = securityParameters.LocalVerifyData;
+
+ state.clientContext.HandshakeComplete(state.client, state.tlsSession);
+
+ recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
+
+ return new DtlsTransport(recordLayer);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual byte[] GenerateCertificateVerify(ClientHandshakeState state,
+ DigitallySigned certificateVerify)
+ {
+ MemoryStream buf = new MemoryStream();
+ certificateVerify.Encode(buf);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual byte[] GenerateClientHello(ClientHandshakeState state)
+ {
+ TlsClientContextImpl context = state.clientContext;
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ context.SetClientSupportedVersions(state.client.GetProtocolVersions());
+
+ ProtocolVersion client_version = ProtocolVersion.GetLatestDtls(context.ClientSupportedVersions);
+ if (!ProtocolVersion.IsSupportedDtlsVersionClient(client_version))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ context.SetClientVersion(client_version);
+
+ byte[] session_id = TlsUtilities.GetSessionID(state.tlsSession);
+
+ bool fallback = state.client.IsFallback();
+
+ state.offeredCipherSuites = state.client.GetCipherSuites();
+
+ if (session_id.Length > 0 && state.sessionParameters != null)
+ {
+ if (!Arrays.Contains(state.offeredCipherSuites, state.sessionParameters.CipherSuite))
+ {
+ session_id = TlsUtilities.EmptyBytes;
+ }
+ }
+
+ state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ state.client.GetClientExtensions());
+
+ ProtocolVersion legacy_version = client_version;
+ if (client_version.IsLaterVersionOf(ProtocolVersion.DTLSv12))
+ {
+ legacy_version = ProtocolVersion.DTLSv12;
+
+ TlsExtensionsUtilities.AddSupportedVersionsExtensionClient(state.clientExtensions,
+ context.ClientSupportedVersions);
+ }
+
+ context.SetRsaPreMasterSecretVersion(legacy_version);
+
+ securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
+ state.clientExtensions);
+
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(client_version))
+ {
+ TlsUtilities.EstablishClientSigAlgs(securityParameters, state.clientExtensions);
+ }
+
+ securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
+ state.clientExtensions);
+
+ state.clientAgreements = TlsUtilities.AddEarlyKeySharesToClientHello(state.clientContext, state.client,
+ state.clientExtensions);
+
+ if (TlsUtilities.IsExtendedMasterSecretOptionalDtls(context.ClientSupportedVersions)
+ && state.client.ShouldUseExtendedMasterSecret())
+ {
+ TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions);
+ }
+ else if (!TlsUtilities.IsTlsV13(client_version)
+ && state.client.RequiresExtendedMasterSecret())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ {
+ bool useGmtUnixTime = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(client_version)
+ && state.client.ShouldUseGmtUnixTime();
+
+ securityParameters.m_clientRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, state.clientContext);
+ }
+
+ // 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 = (null == TlsUtilities.GetExtensionData(state.clientExtensions,
+ ExtensionType.renegotiation_info));
+ bool noRenegScsv = !Arrays.Contains(state.offeredCipherSuites,
+ CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+
+ if (noRenegExt && noRenegScsv)
+ {
+ state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites,
+ CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ }
+ }
+
+ /* (Fallback SCSV)
+ * RFC 7507 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 [..]. (The
+ * client SHOULD put TLS_FALLBACK_SCSV after all cipher suites that it actually intends
+ * to negotiate.)
+ */
+ if (fallback && !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
+ {
+ state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
+ }
+
+ // Heartbeats
+ {
+ state.heartbeat = state.client.GetHeartbeat();
+ state.heartbeatPolicy = state.client.GetHeartbeatPolicy();
+
+ if (null != state.heartbeat || HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy)
+ {
+ TlsExtensionsUtilities.AddHeartbeatExtension(state.clientExtensions,
+ new HeartbeatExtension(state.heartbeatPolicy));
+ }
+ }
+
+
+
+ ClientHello clientHello = new ClientHello(legacy_version, securityParameters.ClientRandom, session_id,
+ TlsUtilities.EmptyBytes, state.offeredCipherSuites, state.clientExtensions);
+
+ MemoryStream buf = new MemoryStream();
+ clientHello.Encode(state.clientContext, buf);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ 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.sessionMasterSecret != null)
+ {
+ state.sessionMasterSecret.Destroy();
+ state.sessionMasterSecret = null;
+ }
+
+ if (state.sessionParameters != null)
+ {
+ state.sessionParameters.Clear();
+ state.sessionParameters = null;
+ }
+
+ if (state.tlsSession != null)
+ {
+ state.tlsSession.Invalidate();
+ state.tlsSession = null;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessCertificateRequest(ClientHandshakeState state, byte[] body)
+ {
+ if (null == state.authentication)
+ {
+ /*
+ * 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);
+
+ CertificateRequest certificateRequest = CertificateRequest.Parse(state.clientContext, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ state.certificateRequest = TlsUtilities.ValidateCertificateRequest(certificateRequest, state.keyExchange);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessCertificateStatus(ClientHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+
+ // TODO[tls13] Ensure this cannot happen for (D)TLS1.3+
+ state.certificateStatus = CertificateStatus.Parse(state.clientContext, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual byte[] ProcessHelloVerifyRequest(ClientHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+
+ ProtocolVersion server_version = TlsUtilities.ReadVersion(buf);
+
+ /*
+ * 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.
+ */
+ int maxCookieLength = ProtocolVersion.DTLSv12.IsEqualOrEarlierVersionOf(server_version) ? 255 : 32;
+
+ byte[] cookie = TlsUtilities.ReadOpaque8(buf, 0, maxCookieLength);
+
+ 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);
+
+ return cookie;
+ }
+
+ /// <exception cref="IOException"/>
+ 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);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessServerCertificate(ClientHandshakeState state, byte[] body)
+ {
+ state.authentication = TlsUtilities.ReceiveServerCertificate(state.clientContext, state.client,
+ new MemoryStream(body, false));
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+
+ ServerHello serverHello = ServerHello.Parse(buf);
+ ProtocolVersion server_version = serverHello.Version;
+
+ state.serverExtensions = serverHello.Extensions;
+
+
+
+ SecurityParameters securityParameters = state.clientContext.SecurityParameters;
+
+ // TODO[dtls13] Check supported_version extension for negotiated version
+
+ ReportServerVersion(state, server_version);
+
+ securityParameters.m_serverRandom = serverHello.Random;
+
+ if (!state.clientContext.ClientVersion.Equals(server_version))
+ {
+ TlsUtilities.CheckDowngradeMarker(server_version, securityParameters.ServerRandom);
+ }
+
+ {
+ byte[] selectedSessionID = serverHello.SessionID;
+ securityParameters.m_sessionID = selectedSessionID;
+ state.client.NotifySessionID(selectedSessionID);
+ state.resumedSession = selectedSessionID.Length > 0 && state.tlsSession != null
+ && Arrays.AreEqual(selectedSessionID, state.tlsSession.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 cipherSuite = ValidateSelectedCipherSuite(serverHello.CipherSuite,
+ AlertDescription.illegal_parameter);
+
+ if (!TlsUtilities.IsValidCipherSuiteSelection(state.offeredCipherSuites, cipherSuite) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ state.client.NotifySelectedCipherSuite(cipherSuite);
+ }
+
+ /*
+ * 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.
+ */
+
+ /*
+ * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+ * master secret [..]. (and see 5.2, 5.3)
+ *
+ * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
+ * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
+ * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
+ */
+ if (TlsUtilities.IsTlsV13(server_version))
+ {
+ securityParameters.m_extendedMasterSecret = true;
+ }
+ else
+ {
+ bool acceptedExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
+ state.serverExtensions);
+
+ if (acceptedExtendedMasterSecret)
+ {
+ if (!state.resumedSession && !state.client.ShouldUseExtendedMasterSecret())
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ else
+ {
+ if (state.client.RequiresExtendedMasterSecret()
+ || (state.resumedSession && !state.client.AllowLegacyResumption()))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ securityParameters.m_extendedMasterSecret = acceptedExtendedMasterSecret;
+ }
+
+ /*
+ *
+ * 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).
+ */
+ securityParameters.m_secureRenegotiation = 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(securityParameters.IsSecureRenegotiation);
+
+ /*
+ * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
+ * contents of this extension are irrelevant, and only the values in the new handshake
+ * messages are considered.
+ */
+ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
+ state.serverExtensions);
+ securityParameters.m_applicationProtocolSet = true;
+
+ // Heartbeats
+ {
+ HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension(
+ state.serverExtensions);
+ if (null == heartbeatExtension)
+ {
+ state.heartbeat = null;
+ state.heartbeatPolicy = HeartbeatMode.peer_not_allowed_to_send;
+ }
+ else if (HeartbeatMode.peer_allowed_to_send != heartbeatExtension.Mode)
+ {
+ state.heartbeat = null;
+ }
+ }
+
+
+
+ IDictionary sessionClientExtensions = state.clientExtensions,
+ sessionServerExtensions = state.serverExtensions;
+
+ if (state.resumedSession)
+ {
+ if (securityParameters.CipherSuite != state.sessionParameters.CipherSuite
+ || !server_version.Equals(state.sessionParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ sessionClientExtensions = null;
+ sessionServerExtensions = state.sessionParameters.ReadServerExtensions();
+ }
+
+ if (sessionServerExtensions != null && sessionServerExtensions.Count > 0)
+ {
+ {
+ /*
+ * 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.m_encryptThenMac = serverSentEncryptThenMac;
+ }
+
+ securityParameters.m_maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession,
+ sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter);
+
+ securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension(
+ sessionServerExtensions);
+
+ if (!state.resumedSession)
+ {
+ // TODO[tls13] See RFC 8446 4.4.2.1
+ if (TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+ ExtensionType.status_request_v2, AlertDescription.illegal_parameter))
+ {
+ securityParameters.m_statusRequestVersion = 2;
+ }
+ else if (TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+ ExtensionType.status_request, AlertDescription.illegal_parameter))
+ {
+ securityParameters.m_statusRequestVersion = 1;
+ }
+ }
+
+ state.expectSessionTicket = !state.resumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+ ExtensionType.session_ticket, AlertDescription.illegal_parameter);
+ }
+
+ if (sessionClientExtensions != null)
+ {
+ state.client.ProcessServerExtensions(sessionServerExtensions);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessServerKeyExchange(ClientHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+ state.keyExchange.ProcessServerKeyExchange(buf);
+ TlsProtocol.AssertEmpty(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessServerSupplementalData(ClientHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+ IList serverSupplementalData = TlsProtocol.ReadSupplementalDataMessage(buf);
+ state.client.ProcessServerSupplementalData(serverSupplementalData);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ReportServerVersion(ClientHandshakeState state, ProtocolVersion server_version)
+ {
+ TlsClientContextImpl context = state.clientContext;
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ ProtocolVersion currentServerVersion = securityParameters.NegotiatedVersion;
+ if (null != currentServerVersion)
+ {
+ if (!currentServerVersion.Equals(server_version))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return;
+ }
+
+ if (!ProtocolVersion.Contains(context.ClientSupportedVersions, server_version))
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+
+ securityParameters.m_negotiatedVersion = server_version;
+
+ TlsUtilities.NegotiatedVersionDtlsClient(state.clientContext, state.client);
+ }
+
+ /// <exception cref="IOException"/>
+ 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(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 TlsSecret sessionMasterSecret = null;
+ internal SessionParameters.Builder sessionParametersBuilder = null;
+ internal int[] offeredCipherSuites = null;
+ internal IDictionary clientExtensions = null;
+ internal IDictionary serverExtensions = null;
+ internal bool resumedSession = false;
+ internal bool expectSessionTicket = false;
+ internal IDictionary clientAgreements = null;
+ internal TlsKeyExchange keyExchange = null;
+ internal TlsAuthentication authentication = null;
+ internal CertificateStatus certificateStatus = null;
+ internal CertificateRequest certificateRequest = null;
+ internal TlsCredentials clientCredentials = null;
+ internal TlsHeartbeat heartbeat = null;
+ internal short heartbeatPolicy = HeartbeatMode.peer_not_allowed_to_send;
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsEpoch.cs b/crypto/src/tls/DtlsEpoch.cs
new file mode 100644
index 000000000..e4ce84948
--- /dev/null
+++ b/crypto/src/tls/DtlsEpoch.cs
@@ -0,0 +1,61 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal sealed class DtlsEpoch
+ {
+ private readonly DtlsReplayWindow m_replayWindow = new DtlsReplayWindow();
+
+ private readonly int m_epoch;
+ private readonly TlsCipher m_cipher;
+
+ private long m_sequenceNumber = 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.m_epoch = epoch;
+ this.m_cipher = cipher;
+ }
+
+ /// <exception cref="IOException"/>
+ internal long AllocateSequenceNumber()
+ {
+ lock (this)
+ {
+ if (m_sequenceNumber >= (1L << 48))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return m_sequenceNumber++;
+ }
+ }
+
+ internal TlsCipher Cipher
+ {
+ get { return m_cipher; }
+ }
+
+ internal int Epoch
+ {
+ get { return m_epoch; }
+ }
+
+ internal DtlsReplayWindow ReplayWindow
+ {
+ get { return m_replayWindow; }
+ }
+
+ internal long SequenceNumber
+ {
+ get { lock (this) return m_sequenceNumber; }
+ set { lock (this) this.m_sequenceNumber = value; }
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsHandshakeRetransmit.cs b/crypto/src/tls/DtlsHandshakeRetransmit.cs
new file mode 100644
index 000000000..6e691b87c
--- /dev/null
+++ b/crypto/src/tls/DtlsHandshakeRetransmit.cs
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal interface DtlsHandshakeRetransmit
+ {
+ /// <exception cref="IOException"/>
+ void ReceivedHandshakeRecord(int epoch, byte[] buf, int off, int len);
+ }
+}
diff --git a/crypto/src/tls/DtlsProtocol.cs b/crypto/src/tls/DtlsProtocol.cs
new file mode 100644
index 000000000..f0f42f968
--- /dev/null
+++ b/crypto/src/tls/DtlsProtocol.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class DtlsProtocol
+ {
+ internal DtlsProtocol()
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ internal 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(maxFragmentLength))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ int plainTextLimit = 1 << (8 + maxFragmentLength);
+ recordLayer.SetPlaintextLimit(plainTextLimit);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal static short EvaluateMaxFragmentLengthExtension(bool resumedSession, IDictionary clientExtensions,
+ IDictionary serverExtensions, short alertDescription)
+ {
+ short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions);
+ if (maxFragmentLength >= 0)
+ {
+ if (!MaxFragmentLength.IsValid(maxFragmentLength)
+ || (!resumedSession && maxFragmentLength != TlsExtensionsUtilities
+ .GetMaxFragmentLengthExtension(clientExtensions)))
+ {
+ throw new TlsFatalAlert(alertDescription);
+ }
+ }
+ return maxFragmentLength;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] GenerateCertificate(TlsContext context, Certificate certificate, Stream endPointHash)
+ {
+ MemoryStream buf = new MemoryStream();
+ certificate.Encode(context, buf, endPointHash);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] GenerateSupplementalData(IList supplementalData)
+ {
+ MemoryStream buf = new MemoryStream();
+ TlsProtocol.WriteSupplementalData(buf, supplementalData);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void SendCertificateMessage(TlsContext context, DtlsReliableHandshake handshake,
+ Certificate certificate, Stream endPointHash)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ if (null != securityParameters.LocalCertificate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (null == certificate)
+ {
+ certificate = Certificate.EmptyChain;
+ }
+
+ byte[] certificateBody = GenerateCertificate(context, certificate, endPointHash);
+ handshake.SendMessage(HandshakeType.certificate, certificateBody);
+
+ securityParameters.m_localCertificate = certificate;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static int ValidateSelectedCipherSuite(int selectedCipherSuite, short alertDescription)
+ {
+ switch (TlsUtilities.GetEncryptionAlgorithm(selectedCipherSuite))
+ {
+ case EncryptionAlgorithm.RC4_40:
+ case EncryptionAlgorithm.RC4_128:
+ case -1:
+ throw new TlsFatalAlert(alertDescription);
+ default:
+ return selectedCipherSuite;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsReassembler.cs b/crypto/src/tls/DtlsReassembler.cs
new file mode 100644
index 000000000..964c8eb84
--- /dev/null
+++ b/crypto/src/tls/DtlsReassembler.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal sealed class DtlsReassembler
+ {
+ private readonly short m_msg_type;
+ private readonly byte[] m_body;
+
+ private readonly IList m_missing = Platform.CreateArrayList();
+
+ internal DtlsReassembler(short msg_type, int length)
+ {
+ this.m_msg_type = msg_type;
+ this.m_body = new byte[length];
+ this.m_missing.Add(new Range(0, length));
+ }
+
+ internal short MsgType
+ {
+ get { return m_msg_type; }
+ }
+
+ internal byte[] GetBodyIfComplete()
+ {
+ return m_missing.Count > 0 ? null : m_body;
+ }
+
+ internal void ContributeFragment(short msg_type, int length, byte[] buf, int off, int fragment_offset,
+ int fragment_length)
+ {
+ int fragment_end = fragment_offset + fragment_length;
+
+ if (m_msg_type != msg_type || m_body.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 && m_missing.Count > 0)
+ {
+ Range firstRange = (Range)m_missing[0];
+ if (firstRange.End == 0)
+ {
+ m_missing.RemoveAt(0);
+ }
+ }
+ return;
+ }
+
+ for (int i = 0; i < m_missing.Count; ++i)
+ {
+ Range range = (Range)m_missing[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, m_body, copyStart, copyLength);
+
+ if (copyStart == range.Start)
+ {
+ if (copyEnd == range.End)
+ {
+ m_missing.RemoveAt(i--);
+ }
+ else
+ {
+ range.Start = copyEnd;
+ }
+ }
+ else
+ {
+ if (copyEnd != range.End)
+ {
+ m_missing.Insert(++i, new Range(copyEnd, range.End));
+ }
+ range.End = copyStart;
+ }
+ }
+ }
+ }
+
+ internal void Reset()
+ {
+ m_missing.Clear();
+ m_missing.Add(new Range(0, m_body.Length));
+ }
+
+ private sealed class Range
+ {
+ private int m_start, m_end;
+
+ internal Range(int start, int end)
+ {
+ this.m_start = start;
+ this.m_end = end;
+ }
+
+ public int Start
+ {
+ get { return m_start; }
+ set { this.m_start = value; }
+ }
+
+ public int End
+ {
+ get { return m_end; }
+ set { this.m_end = value; }
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsRecordLayer.cs b/crypto/src/tls/DtlsRecordLayer.cs
new file mode 100644
index 000000000..ffc071967
--- /dev/null
+++ b/crypto/src/tls/DtlsRecordLayer.cs
@@ -0,0 +1,817 @@
+using System;
+using System.IO;
+#if !PORTABLE || DOTNET
+using System.Net.Sockets;
+#endif
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.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;
+
+ /// <exception cref="IOException"/>
+ internal static byte[] ReceiveClientHelloRecord(byte[] data, int dataOff, int dataLen)
+ {
+ if (dataLen < RECORD_HEADER_LENGTH)
+ {
+ return null;
+ }
+
+ short contentType = TlsUtilities.ReadUint8(data, dataOff + 0);
+ if (ContentType.handshake != contentType)
+ return null;
+
+ ProtocolVersion version = TlsUtilities.ReadVersion(data, dataOff + 1);
+ if (!ProtocolVersion.DTLSv10.IsEqualOrEarlierVersionOf(version))
+ return null;
+
+ int epoch = TlsUtilities.ReadUint16(data, dataOff + 3);
+ if (0 != epoch)
+ return null;
+
+ //long sequenceNumber = TlsUtilities.ReadUint48(data, dataOff + 5);
+
+ int length = TlsUtilities.ReadUint16(data, dataOff + 11);
+ if (dataLen < RECORD_HEADER_LENGTH + length)
+ return null;
+
+ if (length > MAX_FRAGMENT_LENGTH)
+ return null;
+
+ // NOTE: We ignore/drop any data after the first record
+ return TlsUtilities.CopyOfRangeExact(data, dataOff + RECORD_HEADER_LENGTH,
+ dataOff + RECORD_HEADER_LENGTH + length);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void SendHelloVerifyRequestRecord(DatagramSender sender, long recordSeq, byte[] message)
+ {
+ TlsUtilities.CheckUint16(message.Length);
+
+ byte[] record = new byte[RECORD_HEADER_LENGTH + message.Length];
+ TlsUtilities.WriteUint8(ContentType.handshake, record, 0);
+ TlsUtilities.WriteVersion(ProtocolVersion.DTLSv10, record, 1);
+ TlsUtilities.WriteUint16(0, record, 3);
+ TlsUtilities.WriteUint48(recordSeq, record, 5);
+ TlsUtilities.WriteUint16(message.Length, record, 11);
+
+ Array.Copy(message, 0, record, RECORD_HEADER_LENGTH, message.Length);
+
+ SendDatagram(sender, record, 0, record.Length);
+ }
+
+ /// <exception cref="IOException"/>
+ private static void SendDatagram(DatagramSender sender, byte[] buf, int off, int len)
+ {
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //try
+ //{
+ // sender.Send(buf, off, len);
+ //}
+ //catch (InterruptedIOException e)
+ //{
+ // e.bytesTransferred = 0;
+ // throw e;
+ //}
+
+ sender.Send(buf, off, len);
+ }
+
+ private readonly TlsContext m_context;
+ private readonly TlsPeer m_peer;
+ private readonly DatagramTransport m_transport;
+
+ private readonly ByteQueue m_recordQueue = new ByteQueue();
+ private readonly object m_writeLock = new object();
+
+ private volatile bool m_closed = false;
+ private volatile bool m_failed = false;
+ // TODO[dtls13] Review the draft/RFC (legacy_record_version) to see if readVersion can be removed
+ private volatile ProtocolVersion m_readVersion = null, m_writeVersion = null;
+ private volatile bool m_inConnection;
+ private volatile bool m_inHandshake;
+ private volatile int m_plaintextLimit;
+ private DtlsEpoch m_currentEpoch, m_pendingEpoch;
+ private DtlsEpoch m_readEpoch, m_writeEpoch;
+
+ private DtlsHandshakeRetransmit m_retransmit = null;
+ private DtlsEpoch m_retransmitEpoch = null;
+ private Timeout m_retransmitTimeout = null;
+
+ private TlsHeartbeat m_heartbeat = null; // If non-null, controls the sending of heartbeat requests
+ private bool m_heartBeatResponder = false; // Whether we should send heartbeat responses
+
+ private HeartbeatMessage m_heartbeatInFlight = null; // The current in-flight heartbeat request, if any
+ private Timeout m_heartbeatTimeout = null; // Idle timeout (if none in-flight), else expiry timeout for response
+
+ private int m_heartbeatResendMillis = -1; // Delay before retransmit of current in-flight heartbeat request
+ private Timeout m_heartbeatResendTimeout = null; // Timeout for next retransmit of the in-flight heartbeat request
+
+ internal DtlsRecordLayer(TlsContext context, TlsPeer peer, DatagramTransport transport)
+ {
+ this.m_context = context;
+ this.m_peer = peer;
+ this.m_transport = transport;
+
+ this.m_inHandshake = true;
+
+ this.m_currentEpoch = new DtlsEpoch(0, TlsNullNullCipher.Instance);
+ this.m_pendingEpoch = null;
+ this.m_readEpoch = m_currentEpoch;
+ this.m_writeEpoch = m_currentEpoch;
+
+ SetPlaintextLimit(MAX_FRAGMENT_LENGTH);
+ }
+
+ internal virtual bool IsClosed
+ {
+ get { return m_closed; }
+ }
+
+ internal virtual void ResetAfterHelloVerifyRequestServer(long recordSeq)
+ {
+ this.m_inConnection = true;
+
+ m_currentEpoch.SequenceNumber = recordSeq;
+ m_currentEpoch.ReplayWindow.Reset(recordSeq);
+ }
+
+ internal virtual void SetPlaintextLimit(int plaintextLimit)
+ {
+ this.m_plaintextLimit = plaintextLimit;
+ }
+
+ internal virtual int ReadEpoch
+ {
+ get { return m_readEpoch.Epoch; }
+ }
+
+ internal virtual ProtocolVersion ReadVersion
+ {
+ get { return m_readVersion; }
+ set { this.m_readVersion = value; }
+ }
+
+ internal virtual void SetWriteVersion(ProtocolVersion writeVersion)
+ {
+ this.m_writeVersion = writeVersion;
+ }
+
+ internal virtual void InitPendingEpoch(TlsCipher pendingCipher)
+ {
+ if (m_pendingEpoch != 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.m_pendingEpoch = new DtlsEpoch(m_writeEpoch.Epoch + 1, pendingCipher);
+ }
+
+ internal virtual void HandshakeSuccessful(DtlsHandshakeRetransmit retransmit)
+ {
+ if (m_readEpoch == m_currentEpoch || m_writeEpoch == m_currentEpoch)
+ {
+ // TODO
+ throw new InvalidOperationException();
+ }
+
+ if (null != retransmit)
+ {
+ this.m_retransmit = retransmit;
+ this.m_retransmitEpoch = m_currentEpoch;
+ this.m_retransmitTimeout = new Timeout(RETRANSMIT_TIMEOUT);
+ }
+
+ this.m_inHandshake = false;
+ this.m_currentEpoch = m_pendingEpoch;
+ this.m_pendingEpoch = null;
+ }
+
+ internal virtual void InitHeartbeat(TlsHeartbeat heartbeat, bool heartbeatResponder)
+ {
+ if (m_inHandshake)
+ throw new InvalidOperationException();
+
+ this.m_heartbeat = heartbeat;
+ this.m_heartBeatResponder = heartbeatResponder;
+
+ if (null != heartbeat)
+ {
+ ResetHeartbeat();
+ }
+ }
+
+ internal virtual void ResetWriteEpoch()
+ {
+ if (null != m_retransmitEpoch)
+ {
+ this.m_writeEpoch = m_retransmitEpoch;
+ }
+ else
+ {
+ this.m_writeEpoch = m_currentEpoch;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual int GetReceiveLimit()
+ {
+ return System.Math.Min(m_plaintextLimit,
+ m_readEpoch.Cipher.GetPlaintextLimit(m_transport.GetReceiveLimit() - RECORD_HEADER_LENGTH));
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual int GetSendLimit()
+ {
+ return System.Math.Min(m_plaintextLimit,
+ m_writeEpoch.Cipher.GetPlaintextLimit(m_transport.GetSendLimit() - RECORD_HEADER_LENGTH));
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ long currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
+
+ Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis);
+ byte[] record = null;
+
+ while (waitMillis >= 0)
+ {
+ if (null != m_retransmitTimeout && m_retransmitTimeout.RemainingMillis(currentTimeMillis) < 1)
+ {
+ m_retransmit = null;
+ m_retransmitEpoch = null;
+ m_retransmitTimeout = null;
+ }
+
+ if (Timeout.HasExpired(m_heartbeatTimeout, currentTimeMillis))
+ {
+ if (null != m_heartbeatInFlight)
+ throw new TlsTimeoutException("Heartbeat timed out");
+
+ this.m_heartbeatInFlight = HeartbeatMessage.Create(m_context,
+ HeartbeatMessageType.heartbeat_request, m_heartbeat.GeneratePayload());
+ this.m_heartbeatTimeout = new Timeout(m_heartbeat.TimeoutMillis, currentTimeMillis);
+
+ this.m_heartbeatResendMillis = DtlsReliableHandshake.INITIAL_RESEND_MILLIS;
+ this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis);
+
+ SendHeartbeatMessage(m_heartbeatInFlight);
+ }
+ else if (Timeout.HasExpired(m_heartbeatResendTimeout, currentTimeMillis))
+ {
+ this.m_heartbeatResendMillis = DtlsReliableHandshake.BackOff(m_heartbeatResendMillis);
+ this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis);
+
+ SendHeartbeatMessage(m_heartbeatInFlight);
+ }
+
+ waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatTimeout, currentTimeMillis);
+ waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatResendTimeout, currentTimeMillis);
+
+ // NOTE: Guard against bad logic giving a negative value
+ if (waitMillis < 0)
+ {
+ waitMillis = 1;
+ }
+
+ int receiveLimit = System.Math.Min(len, GetReceiveLimit()) + RECORD_HEADER_LENGTH;
+ if (null == record || record.Length < receiveLimit)
+ {
+ record = new byte[receiveLimit];
+ }
+
+ int received = ReceiveRecord(record, 0, receiveLimit, waitMillis);
+ int processed = ProcessRecord(received, record, buf, off);
+ if (processed >= 0)
+ {
+ return processed;
+ }
+
+ currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
+ waitMillis = Timeout.GetWaitMillis(timeout, currentTimeMillis);
+ }
+
+ return -1;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void Send(byte[] buf, int off, int len)
+ {
+ short contentType = ContentType.application_data;
+
+ if (m_inHandshake || m_writeEpoch == m_retransmitEpoch)
+ {
+ contentType = ContentType.handshake;
+
+ short handshakeType = TlsUtilities.ReadUint8(buf, off);
+ if (handshakeType == HandshakeType.finished)
+ {
+ DtlsEpoch nextEpoch = null;
+ if (m_inHandshake)
+ {
+ nextEpoch = m_pendingEpoch;
+ }
+ else if (m_writeEpoch == m_retransmitEpoch)
+ {
+ nextEpoch = m_currentEpoch;
+ }
+
+ 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);
+
+ this.m_writeEpoch = nextEpoch;
+ }
+ }
+
+ SendRecord(contentType, buf, off, len);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void Close()
+ {
+ if (!m_closed)
+ {
+ if (m_inHandshake && m_inConnection)
+ {
+ Warn(AlertDescription.user_canceled, "User canceled handshake");
+ }
+ CloseTransport();
+ }
+ }
+
+ internal virtual void Fail(short alertDescription)
+ {
+ if (!m_closed)
+ {
+ if (m_inConnection)
+ {
+ try
+ {
+ RaiseAlert(AlertLevel.fatal, alertDescription, null, null);
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+ }
+
+ this.m_failed = true;
+
+ CloseTransport();
+ }
+ }
+
+ internal virtual void Failed()
+ {
+ if (!m_closed)
+ {
+ this.m_failed = true;
+
+ CloseTransport();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal virtual void Warn(short alertDescription, String message)
+ {
+ RaiseAlert(AlertLevel.warning, alertDescription, message, null);
+ }
+
+ private void CloseTransport()
+ {
+ if (!m_closed)
+ {
+ /*
+ * 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 (!m_failed)
+ {
+ Warn(AlertDescription.close_notify, null);
+ }
+ m_transport.Close();
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+
+ this.m_closed = true;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private void RaiseAlert(short alertLevel, short alertDescription, string message, Exception cause)
+ {
+ m_peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
+
+ byte[] error = new byte[2];
+ error[0] = (byte)alertLevel;
+ error[1] = (byte)alertDescription;
+
+ SendRecord(ContentType.alert, error, 0, 2);
+ }
+
+ /// <exception cref="IOException"/>
+ private int ReceiveDatagram(byte[] buf, int off, int len, int waitMillis)
+ {
+ try
+ {
+ return m_transport.Receive(buf, off, len, waitMillis);
+ }
+ catch (TlsTimeoutException)
+ {
+ return -1;
+ }
+#if !PORTABLE || DOTNET
+ catch (SocketException e)
+ {
+ if (TlsUtilities.IsTimeout(e))
+ return -1;
+
+ throw e;
+ }
+#endif
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //catch (InterruptedIOException e)
+ //{
+ // e.bytesTransferred = 0;
+ // throw e;
+ //}
+ }
+
+ // TODO Include 'currentTimeMillis' as an argument, use with Timeout, resetHeartbeat
+ /// <exception cref="IOException"/>
+ private int ProcessRecord(int received, byte[] record, byte[] buf, int off)
+ {
+ // NOTE: received < 0 (timeout) is covered by this first case
+ if (received < RECORD_HEADER_LENGTH)
+ return -1;
+
+ int length = TlsUtilities.ReadUint16(record, 11);
+ if (received != (length + RECORD_HEADER_LENGTH))
+ return -1;
+
+ // TODO[dtls13] Deal with opaque record type for 1.3 AEAD ciphers
+ short recordType = TlsUtilities.ReadUint8(record, 0);
+
+ switch (recordType)
+ {
+ case ContentType.alert:
+ case ContentType.application_data:
+ case ContentType.change_cipher_spec:
+ case ContentType.handshake:
+ case ContentType.heartbeat:
+ break;
+ default:
+ return -1;
+ }
+
+ int epoch = TlsUtilities.ReadUint16(record, 3);
+
+ DtlsEpoch recordEpoch = null;
+ if (epoch == m_readEpoch.Epoch)
+ {
+ recordEpoch = m_readEpoch;
+ }
+ else if (recordType == ContentType.handshake && null != m_retransmitEpoch
+ && epoch == m_retransmitEpoch.Epoch)
+ {
+ recordEpoch = m_retransmitEpoch;
+ }
+
+ if (null == recordEpoch)
+ return -1;
+
+ long seq = TlsUtilities.ReadUint48(record, 5);
+ if (recordEpoch.ReplayWindow.ShouldDiscard(seq))
+ return -1;
+
+ ProtocolVersion recordVersion = TlsUtilities.ReadVersion(record, 1);
+ if (!recordVersion.IsDtls)
+ return -1;
+
+ if (null != m_readVersion && !m_readVersion.Equals(recordVersion))
+ {
+ /*
+ * Special-case handling for retransmitted ClientHello records.
+ *
+ * TODO Revisit how 'readVersion' works, since this is quite awkward.
+ */
+ bool isClientHelloFragment =
+ ReadEpoch == 0
+ && length > 0
+ && ContentType.handshake == recordType
+ && HandshakeType.client_hello == TlsUtilities.ReadUint8(record, RECORD_HEADER_LENGTH);
+
+ if (!isClientHelloFragment)
+ return -1;
+ }
+
+ long macSeqNo = GetMacSequenceNumber(recordEpoch.Epoch, seq);
+
+ TlsDecodeResult decoded = recordEpoch.Cipher.DecodeCiphertext(macSeqNo, recordType, recordVersion, record,
+ RECORD_HEADER_LENGTH, length);
+
+ recordEpoch.ReplayWindow.ReportAuthenticated(seq);
+
+ if (decoded.len > m_plaintextLimit)
+ return -1;
+
+ if (decoded.len < 1 && decoded.contentType != ContentType.application_data)
+ return -1;
+
+ if (null == m_readVersion)
+ {
+ bool isHelloVerifyRequest =
+ ReadEpoch == 0
+ && length > 0
+ && ContentType.handshake == recordType
+ && HandshakeType.hello_verify_request == TlsUtilities.ReadUint8(record, RECORD_HEADER_LENGTH);
+
+ if (isHelloVerifyRequest)
+ {
+ /*
+ * 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 (!ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(recordVersion))
+ return -1;
+ }
+ else
+ {
+ this.m_readVersion = recordVersion;
+ }
+ }
+
+ switch (decoded.contentType)
+ {
+ case ContentType.alert:
+ {
+ if (decoded.len == 2)
+ {
+ short alertLevel = TlsUtilities.ReadUint8(decoded.buf, decoded.off);
+ short alertDescription = TlsUtilities.ReadUint8(decoded.buf, decoded.off + 1);
+
+ m_peer.NotifyAlertReceived(alertLevel, alertDescription);
+
+ if (alertLevel == AlertLevel.fatal)
+ {
+ Failed();
+ throw new TlsFatalAlert(alertDescription);
+ }
+
+ // TODO Can close_notify be a fatal alert?
+ if (alertDescription == AlertDescription.close_notify)
+ {
+ CloseTransport();
+ }
+ }
+
+ return -1;
+ }
+ case ContentType.application_data:
+ {
+ if (m_inHandshake)
+ {
+ // TODO Consider buffering application data for new epoch that arrives
+ // out-of-order with the Finished message
+ return -1;
+ }
+ break;
+ }
+ case ContentType.change_cipher_spec:
+ {
+ // Implicitly receive change_cipher_spec and change to pending cipher state
+
+ for (int i = 0; i < decoded.len; ++i)
+ {
+ short message = TlsUtilities.ReadUint8(decoded.buf, decoded.off + i);
+ if (message != ChangeCipherSpec.change_cipher_spec)
+ continue;
+
+ if (m_pendingEpoch != null)
+ {
+ m_readEpoch = m_pendingEpoch;
+ }
+ }
+
+ return -1;
+ }
+ case ContentType.handshake:
+ {
+ if (!m_inHandshake)
+ {
+ if (null != m_retransmit)
+ {
+ m_retransmit.ReceivedHandshakeRecord(epoch, decoded.buf, decoded.off, decoded.len);
+ }
+
+ // TODO Consider support for HelloRequest
+ return -1;
+ }
+ break;
+ }
+ case ContentType.heartbeat:
+ {
+ if (null != m_heartbeatInFlight || m_heartBeatResponder)
+ {
+ try
+ {
+ MemoryStream input = new MemoryStream(decoded.buf, decoded.off, decoded.len, false);
+ HeartbeatMessage heartbeatMessage = HeartbeatMessage.Parse(input);
+
+ if (null != heartbeatMessage)
+ {
+ switch (heartbeatMessage.Type)
+ {
+ case HeartbeatMessageType.heartbeat_request:
+ {
+ if (m_heartBeatResponder)
+ {
+ HeartbeatMessage response = HeartbeatMessage.Create(m_context,
+ HeartbeatMessageType.heartbeat_response, heartbeatMessage.Payload);
+
+ SendHeartbeatMessage(response);
+ }
+ break;
+ }
+ case HeartbeatMessageType.heartbeat_response:
+ {
+ if (null != m_heartbeatInFlight
+ && Arrays.AreEqual(heartbeatMessage.Payload, m_heartbeatInFlight.Payload))
+ {
+ ResetHeartbeat();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+ }
+
+ return -1;
+ }
+ default:
+ return -1;
+ }
+
+ /*
+ * NOTE: If we receive any non-handshake data in the new epoch implies the peer has
+ * received our final flight.
+ */
+ if (!m_inHandshake && null != m_retransmit)
+ {
+ this.m_retransmit = null;
+ this.m_retransmitEpoch = null;
+ this.m_retransmitTimeout = null;
+ }
+
+ Array.Copy(decoded.buf, decoded.off, buf, off, decoded.len);
+ return decoded.len;
+ }
+
+ /// <exception cref="IOException"/>
+ private int ReceiveRecord(byte[] buf, int off, int len, int waitMillis)
+ {
+ if (m_recordQueue.Available > 0)
+ {
+ int length = 0;
+ if (m_recordQueue.Available >= RECORD_HEADER_LENGTH)
+ {
+ byte[] lengthBytes = new byte[2];
+ m_recordQueue.Read(lengthBytes, 0, 2, 11);
+ length = TlsUtilities.ReadUint16(lengthBytes, 0);
+ }
+
+ int received = System.Math.Min(m_recordQueue.Available, RECORD_HEADER_LENGTH + length);
+ m_recordQueue.RemoveData(buf, off, received, 0);
+ return received;
+ }
+
+ {
+ int received = ReceiveDatagram(buf, off, len, waitMillis);
+ if (received >= RECORD_HEADER_LENGTH)
+ {
+ this.m_inConnection = true;
+
+ int fragmentLength = TlsUtilities.ReadUint16(buf, off + 11);
+ int recordLength = RECORD_HEADER_LENGTH + fragmentLength;
+ if (received > recordLength)
+ {
+ m_recordQueue.AddData(buf, off + recordLength, received - recordLength);
+ received = recordLength;
+ }
+ }
+
+ return received;
+ }
+ }
+
+ private void ResetHeartbeat()
+ {
+ this.m_heartbeatInFlight = null;
+ this.m_heartbeatResendMillis = -1;
+ this.m_heartbeatResendTimeout = null;
+ this.m_heartbeatTimeout = new Timeout(m_heartbeat.IdleMillis);
+ }
+
+ /// <exception cref="IOException"/>
+ private void SendHeartbeatMessage(HeartbeatMessage heartbeatMessage)
+ {
+ MemoryStream output = new MemoryStream();
+ heartbeatMessage.Encode(output);
+ byte[] buf = output.ToArray();
+
+ SendRecord(ContentType.heartbeat, buf, 0, buf.Length);
+ }
+
+ /*
+ * Currently uses synchronization to ensure heartbeat sends and application data sends don't
+ * interfere with each other. It may be overly cautious; the sequence number allocation is
+ * atomic, and if we synchronize only on the datagram send instead, then the only effect should
+ * be possible reordering of records (which might surprise a reliable transport implementation).
+ */
+ /// <exception cref="IOException"/>
+ private void SendRecord(short contentType, byte[] buf, int off, int len)
+ {
+ // Never send anything until a valid ClientHello has been received
+ if (m_writeVersion == null)
+ return;
+
+ if (len > m_plaintextLimit)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ /*
+ * RFC 5246 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);
+
+ lock (m_writeLock)
+ {
+ int recordEpoch = m_writeEpoch.Epoch;
+ long recordSequenceNumber = m_writeEpoch.AllocateSequenceNumber();
+ long macSequenceNumber = GetMacSequenceNumber(recordEpoch, recordSequenceNumber);
+ ProtocolVersion recordVersion = m_writeVersion;
+
+ TlsEncodeResult encoded = m_writeEpoch.Cipher.EncodePlaintext(macSequenceNumber, contentType,
+ recordVersion, RECORD_HEADER_LENGTH, buf, off, len);
+
+ int ciphertextLength = encoded.len - RECORD_HEADER_LENGTH;
+ TlsUtilities.CheckUint16(ciphertextLength);
+
+ TlsUtilities.WriteUint8(encoded.recordType, encoded.buf, encoded.off + 0);
+ TlsUtilities.WriteVersion(recordVersion, encoded.buf, encoded.off + 1);
+ TlsUtilities.WriteUint16(recordEpoch, encoded.buf, encoded.off + 3);
+ TlsUtilities.WriteUint48(recordSequenceNumber, encoded.buf, encoded.off + 5);
+ TlsUtilities.WriteUint16(ciphertextLength, encoded.buf, encoded.off + 11);
+
+ SendDatagram(m_transport, encoded.buf, encoded.off, encoded.len);
+ }
+ }
+
+ private static long GetMacSequenceNumber(int epoch, long sequence_number)
+ {
+ return ((epoch & 0xFFFFFFFFL) << 48) | sequence_number;
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsReliableHandshake.cs b/crypto/src/tls/DtlsReliableHandshake.cs
new file mode 100644
index 000000000..b2f8f130a
--- /dev/null
+++ b/crypto/src/tls/DtlsReliableHandshake.cs
@@ -0,0 +1,558 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class DtlsReliableHandshake
+ {
+ private const int MAX_RECEIVE_AHEAD = 16;
+ private const int MESSAGE_HEADER_LENGTH = 12;
+
+ internal const int INITIAL_RESEND_MILLIS = 1000;
+ private const int MAX_RESEND_MILLIS = 60000;
+
+ /// <exception cref="IOException"/>
+ internal static DtlsRequest ReadClientRequest(byte[] data, int dataOff, int dataLen, Stream dtlsOutput)
+ {
+ // TODO Support the possibility of a fragmented ClientHello datagram
+
+ byte[] message = DtlsRecordLayer.ReceiveClientHelloRecord(data, dataOff, dataLen);
+ if (null == message || message.Length < MESSAGE_HEADER_LENGTH)
+ return null;
+
+ long recordSeq = TlsUtilities.ReadUint48(data, dataOff + 5);
+
+ short msgType = TlsUtilities.ReadUint8(message, 0);
+ if (HandshakeType.client_hello != msgType)
+ return null;
+
+ int length = TlsUtilities.ReadUint24(message, 1);
+ if (message.Length != MESSAGE_HEADER_LENGTH + length)
+ return null;
+
+ // TODO Consider stricter HelloVerifyRequest-related checks
+ //int messageSeq = TlsUtilities.ReadUint16(message, 4);
+ //if (messageSeq > 1)
+ // return null;
+
+ int fragmentOffset = TlsUtilities.ReadUint24(message, 6);
+ if (0 != fragmentOffset)
+ return null;
+
+ int fragmentLength = TlsUtilities.ReadUint24(message, 9);
+ if (length != fragmentLength)
+ return null;
+
+ ClientHello clientHello = ClientHello.Parse(
+ new MemoryStream(message, MESSAGE_HEADER_LENGTH, length, false), dtlsOutput);
+
+ return new DtlsRequest(recordSeq, message, clientHello);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void SendHelloVerifyRequest(DatagramSender sender, long recordSeq, byte[] cookie)
+ {
+ TlsUtilities.CheckUint8(cookie.Length);
+
+ int length = 3 + cookie.Length;
+
+ byte[] message = new byte[MESSAGE_HEADER_LENGTH + length];
+ TlsUtilities.WriteUint8(HandshakeType.hello_verify_request, message, 0);
+ TlsUtilities.WriteUint24(length, message, 1);
+ //TlsUtilities.WriteUint16(0, message, 4);
+ //TlsUtilities.WriteUint24(0, message, 6);
+ TlsUtilities.WriteUint24(length, message, 9);
+
+ // HelloVerifyRequest fields
+ TlsUtilities.WriteVersion(ProtocolVersion.DTLSv10, message, MESSAGE_HEADER_LENGTH + 0);
+ TlsUtilities.WriteOpaque8(cookie, message, MESSAGE_HEADER_LENGTH + 2);
+
+ DtlsRecordLayer.SendHelloVerifyRequestRecord(sender, recordSeq, message);
+ }
+
+ /*
+ * No 'final' modifiers so that it works in earlier JDKs
+ */
+ private DtlsRecordLayer m_recordLayer;
+ private Timeout m_handshakeTimeout;
+
+ private TlsHandshakeHash m_handshakeHash;
+
+ private IDictionary m_currentInboundFlight = Platform.CreateHashtable();
+ private IDictionary m_previousInboundFlight = null;
+ private IList m_outboundFlight = Platform.CreateArrayList();
+
+ private int m_resendMillis = -1;
+ private Timeout m_resendTimeout = null;
+
+ private int m_next_send_seq = 0, m_next_receive_seq = 0;
+
+ internal DtlsReliableHandshake(TlsContext context, DtlsRecordLayer transport, int timeoutMillis,
+ DtlsRequest request)
+ {
+ this.m_recordLayer = transport;
+ this.m_handshakeHash = new DeferredHash(context);
+ this.m_handshakeTimeout = Timeout.ForWaitMillis(timeoutMillis);
+
+ if (null != request)
+ {
+ this.m_resendMillis = INITIAL_RESEND_MILLIS;
+ this.m_resendTimeout = new Timeout(m_resendMillis);
+
+ long recordSeq = request.RecordSeq;
+ int messageSeq = request.MessageSeq;
+ byte[] message = request.Message;
+
+ m_recordLayer.ResetAfterHelloVerifyRequestServer(recordSeq);
+
+ // Simulate a previous flight consisting of the request ClientHello
+ DtlsReassembler reassembler = new DtlsReassembler(HandshakeType.client_hello,
+ message.Length - MESSAGE_HEADER_LENGTH);
+ m_currentInboundFlight[messageSeq] = reassembler;
+
+ // We sent HelloVerifyRequest with (message) sequence number 0
+ this.m_next_send_seq = 1;
+ this.m_next_receive_seq = messageSeq + 1;
+
+ m_handshakeHash.Update(message, 0, message.Length);
+ }
+ }
+
+ internal void ResetAfterHelloVerifyRequestClient()
+ {
+ this.m_currentInboundFlight = Platform.CreateHashtable();
+ this.m_previousInboundFlight = null;
+ this.m_outboundFlight = Platform.CreateArrayList();
+
+ this.m_resendMillis = -1;
+ this.m_resendTimeout = null;
+
+ // We're waiting for ServerHello, always with (message) sequence number 1
+ this.m_next_receive_seq = 1;
+
+ m_handshakeHash.Reset();
+ }
+
+ internal TlsHandshakeHash HandshakeHash
+ {
+ get { return m_handshakeHash; }
+ }
+
+ internal TlsHandshakeHash PrepareToFinish()
+ {
+ TlsHandshakeHash result = m_handshakeHash;
+ this.m_handshakeHash = m_handshakeHash.StopTracking();
+ return result;
+ }
+
+ /// <exception cref="IOException"/>
+ internal void SendMessage(short msg_type, byte[] body)
+ {
+ TlsUtilities.CheckUint24(body.Length);
+
+ if (null != m_resendTimeout)
+ {
+ CheckInboundFlight();
+
+ this.m_resendMillis = -1;
+ this.m_resendTimeout = null;
+
+ m_outboundFlight.Clear();
+ }
+
+ Message message = new Message(m_next_send_seq++, msg_type, body);
+
+ m_outboundFlight.Add(message);
+
+ WriteMessage(message);
+ UpdateHandshakeMessagesDigest(message);
+ }
+
+ /// <exception cref="IOException"/>
+ internal byte[] ReceiveMessageBody(short msg_type)
+ {
+ Message message = ReceiveMessage();
+ if (message.Type != msg_type)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ return message.Body;
+ }
+
+ /// <exception cref="IOException"/>
+ internal Message ReceiveMessage()
+ {
+ long currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
+
+ if (null == m_resendTimeout)
+ {
+ m_resendMillis = INITIAL_RESEND_MILLIS;
+ m_resendTimeout = new Timeout(m_resendMillis, currentTimeMillis);
+
+ PrepareInboundFlight(Platform.CreateHashtable());
+ }
+
+ byte[] buf = null;
+
+ for (;;)
+ {
+ if (m_recordLayer.IsClosed)
+ throw new TlsFatalAlert(AlertDescription.user_canceled);
+
+ Message pending = GetPendingMessage();
+ if (pending != null)
+ return pending;
+
+ if (Timeout.HasExpired(m_handshakeTimeout, currentTimeMillis))
+ throw new TlsTimeoutException("Handshake timed out");
+
+ int waitMillis = Timeout.GetWaitMillis(m_handshakeTimeout, currentTimeMillis);
+ waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_resendTimeout, currentTimeMillis);
+
+ // NOTE: Ensure a finite wait, of at least 1ms
+ if (waitMillis < 1)
+ {
+ waitMillis = 1;
+ }
+
+ int receiveLimit = m_recordLayer.GetReceiveLimit();
+ if (buf == null || buf.Length < receiveLimit)
+ {
+ buf = new byte[receiveLimit];
+ }
+
+ int received = m_recordLayer.Receive(buf, 0, receiveLimit, waitMillis);
+ if (received < 0)
+ {
+ ResendOutboundFlight();
+ }
+ else
+ {
+ ProcessRecord(MAX_RECEIVE_AHEAD, m_recordLayer.ReadEpoch, buf, 0, received);
+ }
+
+ currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
+ }
+ }
+
+ internal void Finish()
+ {
+ DtlsHandshakeRetransmit retransmit = null;
+ if (null != m_resendTimeout)
+ {
+ CheckInboundFlight();
+ }
+ else
+ {
+ PrepareInboundFlight(null);
+
+ if (m_previousInboundFlight != 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);
+ }
+ }
+
+ m_recordLayer.HandshakeSuccessful(retransmit);
+ }
+
+ internal static int BackOff(int timeoutMillis)
+ {
+ /*
+ * TODO[DTLS] implementations SHOULD back off handshake packet size during the
+ * retransmit backoff.
+ */
+ return System.Math.Min(timeoutMillis * 2, MAX_RESEND_MILLIS);
+ }
+
+ /**
+ * Check that there are no "extra" messages left in the current inbound flight
+ */
+ private void CheckInboundFlight()
+ {
+ foreach (int key in m_currentInboundFlight.Keys)
+ {
+ if (key >= m_next_receive_seq)
+ {
+ // TODO Should this be considered an error?
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private Message GetPendingMessage()
+ {
+ DtlsReassembler next = (DtlsReassembler)m_currentInboundFlight[m_next_receive_seq];
+ if (next != null)
+ {
+ byte[] body = next.GetBodyIfComplete();
+ if (body != null)
+ {
+ m_previousInboundFlight = null;
+ return UpdateHandshakeMessagesDigest(new Message(m_next_receive_seq++, next.MsgType, body));
+ }
+ }
+ return null;
+ }
+
+ private void PrepareInboundFlight(IDictionary nextFlight)
+ {
+ ResetAll(m_currentInboundFlight);
+ m_previousInboundFlight = m_currentInboundFlight;
+ m_currentInboundFlight = nextFlight;
+ }
+
+ /// <exception cref="IOException"/>
+ private void ProcessRecord(int windowSize, int epoch, byte[] buf, int off, int len)
+ {
+ bool checkPreviousFlight = false;
+
+ while (len >= MESSAGE_HEADER_LENGTH)
+ {
+ int fragment_length = TlsUtilities.ReadUint24(buf, off + 9);
+ int message_length = fragment_length + MESSAGE_HEADER_LENGTH;
+ if (len < message_length)
+ {
+ // NOTE: Truncated message - ignore it
+ break;
+ }
+
+ int length = TlsUtilities.ReadUint24(buf, off + 1);
+ int fragment_offset = TlsUtilities.ReadUint24(buf, off + 6);
+ if (fragment_offset + fragment_length > length)
+ {
+ // NOTE: Malformed fragment - ignore it and the rest of the record
+ break;
+ }
+
+ /*
+ * NOTE: This very simple epoch check will only work until we want to support
+ * renegotiation (and we're not likely to do that anyway).
+ */
+ short msg_type = TlsUtilities.ReadUint8(buf, off + 0);
+ int expectedEpoch = msg_type == HandshakeType.finished ? 1 : 0;
+ if (epoch != expectedEpoch)
+ break;
+
+ int message_seq = TlsUtilities.ReadUint16(buf, off + 4);
+ if (message_seq >= (m_next_receive_seq + windowSize))
+ {
+ // NOTE: Too far ahead - ignore
+ }
+ else if (message_seq >= m_next_receive_seq)
+ {
+ DtlsReassembler reassembler = (DtlsReassembler)m_currentInboundFlight[message_seq];
+ if (reassembler == null)
+ {
+ reassembler = new DtlsReassembler(msg_type, length);
+ m_currentInboundFlight[message_seq] = reassembler;
+ }
+
+ reassembler.ContributeFragment(msg_type, length, buf, off + MESSAGE_HEADER_LENGTH, fragment_offset,
+ fragment_length);
+ }
+ else if (m_previousInboundFlight != null)
+ {
+ /*
+ * NOTE: If we receive the previous flight of incoming messages in full again,
+ * retransmit our last flight
+ */
+
+ DtlsReassembler reassembler = (DtlsReassembler)m_previousInboundFlight[message_seq];
+ if (reassembler != null)
+ {
+ reassembler.ContributeFragment(msg_type, length, buf, off + MESSAGE_HEADER_LENGTH,
+ fragment_offset, fragment_length);
+ checkPreviousFlight = true;
+ }
+ }
+
+ off += message_length;
+ len -= message_length;
+ }
+
+ if (checkPreviousFlight && CheckAll(m_previousInboundFlight))
+ {
+ ResendOutboundFlight();
+ ResetAll(m_previousInboundFlight);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private void ResendOutboundFlight()
+ {
+ m_recordLayer.ResetWriteEpoch();
+ foreach (Message message in m_outboundFlight)
+ {
+ WriteMessage(message);
+ }
+
+ m_resendMillis = BackOff(m_resendMillis);
+ m_resendTimeout = new Timeout(m_resendMillis);
+ }
+
+ /// <exception cref="IOException"/>
+ private Message UpdateHandshakeMessagesDigest(Message message)
+ {
+ short msg_type = message.Type;
+ switch (msg_type)
+ {
+ case HandshakeType.hello_request:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.key_update:
+ case HandshakeType.new_session_ticket:
+ break;
+
+ default:
+ {
+ byte[] body = message.Body;
+ byte[] buf = new byte[MESSAGE_HEADER_LENGTH];
+ TlsUtilities.WriteUint8(msg_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);
+ m_handshakeHash.Update(buf, 0, buf.Length);
+ m_handshakeHash.Update(body, 0, body.Length);
+ break;
+ }
+ }
+
+ return message;
+ }
+
+ /// <exception cref="IOException"/>
+ private void WriteMessage(Message message)
+ {
+ int sendLimit = m_recordLayer.GetSendLimit();
+ int fragmentLimit = sendLimit - MESSAGE_HEADER_LENGTH;
+
+ // 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);
+ }
+
+ /// <exception cref="IOException"/>
+ private void WriteHandshakeFragment(Message message, int fragment_offset, int fragment_length)
+ {
+ RecordLayerBuffer fragment = new RecordLayerBuffer(MESSAGE_HEADER_LENGTH + 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(m_recordLayer);
+ }
+
+ 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 m_message_seq;
+ private readonly short m_msg_type;
+ private readonly byte[] m_body;
+
+ internal Message(int message_seq, short msg_type, byte[] body)
+ {
+ this.m_message_seq = message_seq;
+ this.m_msg_type = msg_type;
+ this.m_body = body;
+ }
+
+ public int Seq
+ {
+ get { return m_message_seq; }
+ }
+
+ public short Type
+ {
+ get { return m_msg_type; }
+ }
+
+ public byte[] Body
+ {
+ get { return m_body; }
+ }
+ }
+
+ internal class RecordLayerBuffer
+ : MemoryStream
+ {
+ internal RecordLayerBuffer(int size)
+ : base(size)
+ {
+ }
+
+ internal void SendToRecordLayer(DtlsRecordLayer recordLayer)
+ {
+#if PORTABLE
+ byte[] buf = ToArray();
+ int bufLen = buf.Length;
+#else
+ byte[] buf = GetBuffer();
+ int bufLen = (int)Length;
+#endif
+
+ recordLayer.Send(buf, 0, bufLen);
+ Platform.Dispose(this);
+ }
+ }
+
+ internal class Retransmit
+ : DtlsHandshakeRetransmit
+ {
+ private readonly DtlsReliableHandshake m_outer;
+
+ internal Retransmit(DtlsReliableHandshake outer)
+ {
+ this.m_outer = outer;
+ }
+
+ public void ReceivedHandshakeRecord(int epoch, byte[] buf, int off, int len)
+ {
+ m_outer.ProcessRecord(0, epoch, buf, off, len);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsReplayWindow.cs b/crypto/src/tls/DtlsReplayWindow.cs
new file mode 100644
index 000000000..a08114c2a
--- /dev/null
+++ b/crypto/src/tls/DtlsReplayWindow.cs
@@ -0,0 +1,84 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /**
+ * RFC 4347 4.1.2.5 Anti-replay
+ * <p>
+ * Support fast rejection of duplicate records by maintaining a sliding receive window
+ * </p>
+ */
+ internal sealed class DtlsReplayWindow
+ {
+ private const long ValidSeqMask = 0x0000FFFFFFFFFFFFL;
+
+ private const long WindowSize = 64L;
+
+ private long m_latestConfirmedSeq = -1;
+ private ulong m_bitmap = 0;
+
+ /// <summary>Check whether a received record with the given sequence number should be rejected as a duplicate.
+ /// </summary>
+ /// <param name="seq">the 48-bit DTLSPlainText.sequence_number field of a received record.</param>
+ /// <returns>true if the record should be discarded without further processing.</returns>
+ internal bool ShouldDiscard(long seq)
+ {
+ if ((seq & ValidSeqMask) != seq)
+ return true;
+
+ if (seq <= m_latestConfirmedSeq)
+ {
+ long diff = m_latestConfirmedSeq - seq;
+ if (diff >= WindowSize)
+ return true;
+
+ if ((m_bitmap & (1UL << (int)diff)) != 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>Report that a received record with the given sequence number passed authentication checks.
+ /// </summary>
+ /// <param name="seq">the 48-bit DTLSPlainText.sequence_number field of an authenticated record.</param>
+ internal void ReportAuthenticated(long seq)
+ {
+ if ((seq & ValidSeqMask) != seq)
+ throw new ArgumentException("out of range", "seq");
+
+ if (seq <= m_latestConfirmedSeq)
+ {
+ long diff = m_latestConfirmedSeq - seq;
+ if (diff < WindowSize)
+ {
+ m_bitmap |= (1UL << (int)diff);
+ }
+ }
+ else
+ {
+ long diff = seq - m_latestConfirmedSeq;
+ if (diff >= WindowSize)
+ {
+ m_bitmap = 1;
+ }
+ else
+ {
+ m_bitmap <<= (int)diff; // for earlier JDKs
+ m_bitmap |= 1UL;
+ }
+ m_latestConfirmedSeq = seq;
+ }
+ }
+
+ internal void Reset(long seq)
+ {
+ if ((seq & ValidSeqMask) != seq)
+ throw new ArgumentException("out of range", "seq");
+
+ // Discard future records unless sequence number > 'seq'
+ m_latestConfirmedSeq = seq;
+ m_bitmap = ulong.MaxValue >> (int)System.Math.Max(0, 63 - seq);
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsRequest.cs b/crypto/src/tls/DtlsRequest.cs
new file mode 100644
index 000000000..126429e67
--- /dev/null
+++ b/crypto/src/tls/DtlsRequest.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class DtlsRequest
+ {
+ private readonly long m_recordSeq;
+ private readonly byte[] m_message;
+ private readonly ClientHello m_clientHello;
+
+ internal DtlsRequest(long recordSeq, byte[] message, ClientHello clientHello)
+ {
+ this.m_recordSeq = recordSeq;
+ this.m_message = message;
+ this.m_clientHello = clientHello;
+ }
+
+ internal ClientHello ClientHello
+ {
+ get { return m_clientHello; }
+ }
+
+ internal byte[] Message
+ {
+ get { return m_message; }
+ }
+
+ internal int MessageSeq
+ {
+ get { return TlsUtilities.ReadUint16(m_message, 4); }
+ }
+
+ internal long RecordSeq
+ {
+ get { return m_recordSeq; }
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs
new file mode 100644
index 000000000..5637d4106
--- /dev/null
+++ b/crypto/src/tls/DtlsServerProtocol.cs
@@ -0,0 +1,835 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DtlsServerProtocol
+ : DtlsProtocol
+ {
+ protected bool m_verifyRequests = true;
+
+ public DtlsServerProtocol()
+ : base()
+ {
+ }
+
+ public virtual bool VerifyRequests
+ {
+ get { return m_verifyRequests; }
+ set { this.m_verifyRequests = value; }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual DtlsTransport Accept(TlsServer server, DatagramTransport transport)
+ {
+ return Accept(server, transport, null);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual DtlsTransport Accept(TlsServer server, DatagramTransport transport, DtlsRequest request)
+ {
+ if (server == null)
+ throw new ArgumentNullException("server");
+ if (transport == null)
+ throw new ArgumentNullException("transport");
+
+ ServerHandshakeState state = new ServerHandshakeState();
+ state.server = server;
+ state.serverContext = new TlsServerContextImpl(server.Crypto);
+ server.Init(state.serverContext);
+ state.serverContext.HandshakeBeginning(server);
+
+ SecurityParameters securityParameters = state.serverContext.SecurityParameters;
+ securityParameters.m_extendedPadding = server.ShouldUseExtendedPadding();
+
+ DtlsRecordLayer recordLayer = new DtlsRecordLayer(state.serverContext, state.server, transport);
+ server.NotifyCloseHandle(recordLayer);
+
+ try
+ {
+ return ServerHandshake(state, recordLayer, request);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ AbortServerHandshake(state, recordLayer, fatalAlert.AlertDescription);
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ AbortServerHandshake(state, recordLayer, AlertDescription.internal_error);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ AbortServerHandshake(state, recordLayer, AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ finally
+ {
+ securityParameters.Clear();
+ }
+ }
+
+ internal virtual void AbortServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer,
+ short alertDescription)
+ {
+ recordLayer.Fail(alertDescription);
+ InvalidateSession(state);
+ }
+
+ /// <exception cref="IOException"/>
+ internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer,
+ DtlsRequest request)
+ {
+ SecurityParameters securityParameters = state.serverContext.SecurityParameters;
+
+ DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.serverContext, recordLayer,
+ state.server.GetHandshakeTimeoutMillis(), request);
+
+ DtlsReliableHandshake.Message clientMessage = null;
+
+ if (null == request)
+ {
+ clientMessage = handshake.ReceiveMessage();
+
+ // NOTE: DtlsRecordLayer requires any DTLS version, we don't otherwise constrain this
+ //ProtocolVersion recordLayerVersion = recordLayer.ReadVersion;
+
+ if (clientMessage.Type == HandshakeType.client_hello)
+ {
+ ProcessClientHello(state, clientMessage.Body);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+ else
+ {
+ ProcessClientHello(state, request.ClientHello);
+ }
+
+ /*
+ * NOTE: Currently no server support for session resumption
+ *
+ * If adding support, ensure securityParameters.tlsUnique is set to the localVerifyData, but
+ * ONLY when extended_master_secret has been negotiated (otherwise NULL).
+ */
+ {
+ // TODO[resumption]
+
+ state.tlsSession = TlsUtilities.ImportSession(TlsUtilities.EmptyBytes, null);
+ state.sessionParameters = null;
+ state.sessionMasterSecret = null;
+ }
+
+ securityParameters.m_sessionID = state.tlsSession.SessionID;
+
+ state.server.NotifySession(state.tlsSession);
+
+ {
+ byte[] serverHelloBody = GenerateServerHello(state, recordLayer);
+
+ // TODO[dtls13] Ideally, move this into GenerateServerHello once legacy_record_version clarified
+ {
+ ProtocolVersion recordLayerVersion = state.serverContext.ServerVersion;
+ recordLayer.ReadVersion = recordLayerVersion;
+ recordLayer.SetWriteVersion(recordLayerVersion);
+ }
+
+ handshake.SendMessage(HandshakeType.server_hello, serverHelloBody);
+ }
+
+ handshake.HandshakeHash.NotifyPrfDetermined();
+
+ IList serverSupplementalData = state.server.GetServerSupplementalData();
+ if (serverSupplementalData != null)
+ {
+ byte[] supplementalDataBody = GenerateSupplementalData(serverSupplementalData);
+ handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody);
+ }
+
+ state.keyExchange = TlsUtilities.InitKeyExchangeServer(state.serverContext, state.server);
+ state.serverCredentials = TlsUtilities.EstablishServerCredentials(state.server);
+
+ // Server certificate
+ {
+ Certificate serverCertificate = null;
+
+ MemoryStream endPointHash = new MemoryStream();
+ if (state.serverCredentials == null)
+ {
+ state.keyExchange.SkipServerCredentials();
+ }
+ else
+ {
+ state.keyExchange.ProcessServerCredentials(state.serverCredentials);
+
+ serverCertificate = state.serverCredentials.Certificate;
+
+ SendCertificateMessage(state.serverContext, handshake, serverCertificate, endPointHash);
+ }
+ securityParameters.m_tlsServerEndPoint = endPointHash.ToArray();
+
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (serverCertificate == null || serverCertificate.IsEmpty)
+ {
+ securityParameters.m_statusRequestVersion = 0;
+ }
+ }
+
+ if (securityParameters.StatusRequestVersion > 0)
+ {
+ 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 (null == state.certificateRequest)
+ {
+ /*
+ * For static agreement key exchanges, CertificateRequest is required since
+ * the client Certificate message is mandatory but can only be sent if the
+ * server requests it.
+ */
+ if (!state.keyExchange.RequiresCertificateVerify)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ else
+ {
+ if (TlsUtilities.IsTlsV12(state.serverContext)
+ != (state.certificateRequest.SupportedSignatureAlgorithms != null))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ state.certificateRequest = TlsUtilities.ValidateCertificateRequest(state.certificateRequest, state.keyExchange);
+
+ TlsUtilities.EstablishServerSigAlgs(securityParameters, state.certificateRequest);
+
+ TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, securityParameters.ServerSigAlgs);
+
+ byte[] certificateRequestBody = GenerateCertificateRequest(state, state.certificateRequest);
+ handshake.SendMessage(HandshakeType.certificate_request, certificateRequestBody);
+ }
+ }
+
+ handshake.SendMessage(HandshakeType.server_hello_done, TlsUtilities.EmptyBytes);
+
+ bool forceBuffering = false;
+ TlsUtilities.SealHandshakeHash(state.serverContext, handshake.HandshakeHash, forceBuffering);
+
+ 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);
+ }
+
+ securityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(handshake.HandshakeHash);
+
+ TlsProtocol.EstablishMasterSecret(state.serverContext, state.keyExchange);
+ recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(state.serverContext));
+
+ /*
+ * 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).
+ */
+ {
+ TlsHandshakeHash certificateVerifyHash = handshake.PrepareToFinish();
+
+ if (ExpectCertificateVerifyMessage(state))
+ {
+ byte[] certificateVerifyBody = handshake.ReceiveMessageBody(HandshakeType.certificate_verify);
+ ProcessCertificateVerify(state, certificateVerifyBody, certificateVerifyHash);
+ }
+ }
+
+ // NOTE: Calculated exclusive of the actual Finished message from the client
+ securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext,
+ handshake.HandshakeHash, false);
+ ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), securityParameters.PeerVerifyData);
+
+ if (state.expectSessionTicket)
+ {
+ NewSessionTicket newSessionTicket = state.server.GetNewSessionTicket();
+ byte[] newSessionTicketBody = GenerateNewSessionTicket(state, newSessionTicket);
+ handshake.SendMessage(HandshakeType.new_session_ticket, newSessionTicketBody);
+ }
+
+ // NOTE: Calculated exclusive of the Finished message itself
+ securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext,
+ handshake.HandshakeHash, true);
+ handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
+
+ handshake.Finish();
+
+ state.sessionMasterSecret = securityParameters.MasterSecret;
+
+ state.sessionParameters = new SessionParameters.Builder()
+ .SetCipherSuite(securityParameters.CipherSuite)
+ .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
+ .SetLocalCertificate(securityParameters.LocalCertificate)
+ .SetMasterSecret(state.serverContext.Crypto.AdoptSecret(state.sessionMasterSecret))
+ .SetNegotiatedVersion(securityParameters.NegotiatedVersion)
+ .SetPeerCertificate(securityParameters.PeerCertificate)
+ .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);
+
+ securityParameters.m_tlsUnique = securityParameters.PeerVerifyData;
+
+ state.serverContext.HandshakeComplete(state.server, state.tlsSession);
+
+ recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
+
+ return new DtlsTransport(recordLayer);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual byte[] GenerateCertificateRequest(ServerHandshakeState state,
+ CertificateRequest certificateRequest)
+ {
+ MemoryStream buf = new MemoryStream();
+ certificateRequest.Encode(state.serverContext, buf);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual byte[] GenerateCertificateStatus(ServerHandshakeState state,
+ CertificateStatus certificateStatus)
+ {
+ MemoryStream buf = new MemoryStream();
+ // TODO[tls13] Ensure this cannot happen for (D)TLS1.3+
+ certificateStatus.Encode(buf);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual byte[] GenerateNewSessionTicket(ServerHandshakeState state,
+ NewSessionTicket newSessionTicket)
+ {
+ MemoryStream buf = new MemoryStream();
+ newSessionTicket.Encode(buf);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ internal virtual byte[] GenerateServerHello(ServerHandshakeState state, DtlsRecordLayer recordLayer)
+ {
+ TlsServerContextImpl context = state.serverContext;
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ ProtocolVersion server_version = state.server.GetServerVersion();
+ {
+ if (!ProtocolVersion.Contains(context.ClientSupportedVersions, server_version))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ // TODO[dtls13] Read draft/RFC for guidance on the legacy_record_version field
+ //ProtocolVersion legacy_record_version = server_version.IsLaterVersionOf(ProtocolVersion.DTLSv12)
+ // ? ProtocolVersion.DTLSv12
+ // : server_version;
+
+ //recordLayer.SetWriteVersion(legacy_record_version);
+ securityParameters.m_negotiatedVersion = server_version;
+
+ TlsUtilities.NegotiatedVersionDtlsServer(context);
+ }
+
+ {
+ bool useGmtUnixTime = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(server_version)
+ && state.server.ShouldUseGmtUnixTime();
+
+ securityParameters.m_serverRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, context);
+
+ if (!server_version.Equals(ProtocolVersion.GetLatestDtls(state.server.GetProtocolVersions())))
+ {
+ TlsUtilities.WriteDowngradeMarker(server_version, securityParameters.ServerRandom);
+ }
+ }
+
+ {
+ int cipherSuite = ValidateSelectedCipherSuite(state.server.GetSelectedCipherSuite(),
+ AlertDescription.internal_error);
+
+ if (!TlsUtilities.IsValidCipherSuiteSelection(state.offeredCipherSuites, cipherSuite) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ }
+
+ state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ state.server.GetServerExtensions());
+
+ state.server.GetServerExtensionsForConnection(state.serverExtensions);
+
+ ProtocolVersion legacy_version = server_version;
+ if (server_version.IsLaterVersionOf(ProtocolVersion.DTLSv12))
+ {
+ legacy_version = ProtocolVersion.DTLSv12;
+
+ TlsExtensionsUtilities.AddSupportedVersionsExtensionServer(state.serverExtensions, server_version);
+ }
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ if (securityParameters.IsSecureRenegotiation)
+ {
+ 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[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(
+ TlsUtilities.EmptyBytes);
+ }
+ }
+
+ /*
+ * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+ * master secret [..]. (and see 5.2, 5.3)
+ *
+ * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
+ * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
+ * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
+ */
+ if (TlsUtilities.IsTlsV13(server_version))
+ {
+ securityParameters.m_extendedMasterSecret = true;
+ }
+ else
+ {
+ securityParameters.m_extendedMasterSecret = state.offeredExtendedMasterSecret
+ && state.server.ShouldUseExtendedMasterSecret();
+
+ if (securityParameters.IsExtendedMasterSecret)
+ {
+ TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions);
+ }
+ else if (state.server.RequiresExtendedMasterSecret())
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ else if (state.resumedSession && !state.server.AllowLegacyResumption())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ // Heartbeats
+ if (null != state.heartbeat || HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy)
+ {
+ TlsExtensionsUtilities.AddHeartbeatExtension(state.serverExtensions,
+ new HeartbeatExtension(state.heartbeatPolicy));
+ }
+
+
+
+ /*
+ * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
+ * contents of this extension are irrelevant, and only the values in the new handshake
+ * messages are considered.
+ */
+ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
+ state.serverExtensions);
+ securityParameters.m_applicationProtocolSet = true;
+
+ /*
+ * 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.Count > 0)
+ {
+ securityParameters.m_encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(
+ state.serverExtensions);
+
+ securityParameters.m_maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession,
+ state.clientExtensions, state.serverExtensions, AlertDescription.internal_error);
+
+ securityParameters.m_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.
+ */
+ if (!state.resumedSession)
+ {
+ // TODO[tls13] See RFC 8446 4.4.2.1
+ if (TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions,
+ ExtensionType.status_request_v2, AlertDescription.internal_error))
+ {
+ securityParameters.m_statusRequestVersion = 2;
+ }
+ else if (TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions,
+ ExtensionType.status_request, AlertDescription.internal_error))
+ {
+ securityParameters.m_statusRequestVersion = 1;
+ }
+ }
+
+ state.expectSessionTicket = !state.resumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.session_ticket,
+ AlertDescription.internal_error);
+ }
+
+ ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
+
+
+
+ ServerHello serverHello = new ServerHello(legacy_version, securityParameters.ServerRandom,
+ state.tlsSession.SessionID, securityParameters.CipherSuite, state.serverExtensions);
+
+ MemoryStream buf = new MemoryStream();
+ serverHello.Encode(state.serverContext, buf);
+ return buf.ToArray();
+ }
+
+ protected virtual void InvalidateSession(ServerHandshakeState state)
+ {
+ if (state.sessionMasterSecret != null)
+ {
+ state.sessionMasterSecret.Destroy();
+ state.sessionMasterSecret = null;
+ }
+
+ if (state.sessionParameters != null)
+ {
+ state.sessionParameters.Clear();
+ state.sessionParameters = null;
+ }
+
+ if (state.tlsSession != null)
+ {
+ state.tlsSession.Invalidate();
+ state.tlsSession = null;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void NotifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate)
+ {
+ if (null == state.certificateRequest)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsUtilities.ProcessClientCertificate(state.serverContext, clientCertificate, state.keyExchange,
+ state.server);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessClientCertificate(ServerHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+
+ Certificate.ParseOptions options = new Certificate.ParseOptions()
+ .SetMaxChainLength(state.server.GetMaxCertificateChainLength());
+
+ Certificate clientCertificate = Certificate.Parse(options, state.serverContext, buf, null);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ NotifyClientCertificate(state, clientCertificate);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessCertificateVerify(ServerHandshakeState state, byte[] body,
+ TlsHandshakeHash handshakeHash)
+ {
+ if (state.certificateRequest == null)
+ throw new InvalidOperationException();
+
+ MemoryStream buf = new MemoryStream(body, false);
+
+ TlsServerContextImpl context = state.serverContext;
+ DigitallySigned certificateVerify = DigitallySigned.Parse(context, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ TlsUtilities.VerifyCertificateVerifyClient(context, state.certificateRequest, certificateVerify, handshakeHash);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessClientHello(ServerHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+ ClientHello clientHello = ClientHello.Parse(buf, new NullOutputStream());
+ ProcessClientHello(state, clientHello);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessClientHello(ServerHandshakeState state, ClientHello clientHello)
+ {
+ // TODO Read RFCs for guidance on the expected record layer version number
+ ProtocolVersion legacy_version = clientHello.Version;
+ state.offeredCipherSuites = clientHello.CipherSuites;
+
+ /*
+ * 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 = clientHello.Extensions;
+
+
+
+ TlsServerContextImpl context = state.serverContext;
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ if (!legacy_version.IsDtls)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ context.SetRsaPreMasterSecretVersion(legacy_version);
+
+ context.SetClientSupportedVersions(
+ TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(state.clientExtensions));
+
+ ProtocolVersion client_version = legacy_version;
+ if (null == context.ClientSupportedVersions)
+ {
+ if (client_version.IsLaterVersionOf(ProtocolVersion.DTLSv12))
+ {
+ client_version = ProtocolVersion.DTLSv12;
+ }
+
+ context.SetClientSupportedVersions(client_version.DownTo(ProtocolVersion.DTLSv10));
+ }
+ else
+ {
+ client_version = ProtocolVersion.GetLatestDtls(context.ClientSupportedVersions);
+ }
+
+ if (!ProtocolVersion.SERVER_EARLIEST_SUPPORTED_DTLS.IsEqualOrEarlierVersionOf(client_version))
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+
+ context.SetClientVersion(client_version);
+
+ state.server.NotifyClientVersion(context.ClientVersion);
+
+ securityParameters.m_clientRandom = clientHello.Random;
+
+ state.server.NotifyFallback(Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
+
+ state.server.NotifyOfferedCipherSuites(state.offeredCipherSuites);
+
+ /*
+ * TODO[resumption] Check RFC 7627 5.4. for required behaviour
+ */
+
+ /*
+ * 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))
+ {
+ securityParameters.m_secureRenegotiation = 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.
+ */
+ securityParameters.m_secureRenegotiation = true;
+
+ if (!Arrays.ConstantTimeAreEqual(renegExtData,
+ TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ state.server.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
+
+ state.offeredExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
+ state.clientExtensions);
+
+ if (state.clientExtensions != null)
+ {
+ // NOTE: Validates the padding extension data, if present
+ TlsExtensionsUtilities.GetPaddingExtension(state.clientExtensions);
+
+ securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
+ state.clientExtensions);
+
+ /*
+ * 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(client_version))
+ {
+ TlsUtilities.EstablishClientSigAlgs(securityParameters, state.clientExtensions);
+ }
+
+ securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
+ state.clientExtensions);
+
+ // Heartbeats
+ {
+ HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension(
+ state.clientExtensions);
+ if (null != heartbeatExtension)
+ {
+ if (HeartbeatMode.peer_allowed_to_send == heartbeatExtension.Mode)
+ {
+ state.heartbeat = state.server.GetHeartbeat();
+ }
+
+ state.heartbeatPolicy = state.server.GetHeartbeatPolicy();
+ }
+ }
+
+ state.server.ProcessClientExtensions(state.clientExtensions);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessClientKeyExchange(ServerHandshakeState state, byte[] body)
+ {
+ MemoryStream buf = new MemoryStream(body, false);
+ state.keyExchange.ProcessClientKeyExchange(buf);
+ TlsProtocol.AssertEmpty(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ 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)
+ {
+ if (null == state.certificateRequest)
+ return false;
+
+ Certificate clientCertificate = state.serverContext.SecurityParameters.PeerCertificate;
+
+ return null != clientCertificate && !clientCertificate.IsEmpty
+ && (null == state.keyExchange || state.keyExchange.RequiresCertificateVerify);
+ }
+
+ protected internal class ServerHandshakeState
+ {
+ internal TlsServer server = null;
+ internal TlsServerContextImpl serverContext = null;
+ internal TlsSession tlsSession = null;
+ internal SessionParameters sessionParameters = null;
+ internal TlsSecret sessionMasterSecret = null;
+ internal SessionParameters.Builder sessionParametersBuilder = null;
+ internal int[] offeredCipherSuites = null;
+ internal IDictionary clientExtensions = null;
+ internal IDictionary serverExtensions = null;
+ internal bool offeredExtendedMasterSecret = false;
+ internal bool resumedSession = false;
+ internal bool expectSessionTicket = false;
+ internal TlsKeyExchange keyExchange = null;
+ internal TlsCredentials serverCredentials = null;
+ internal CertificateRequest certificateRequest = null;
+ internal TlsHeartbeat heartbeat = null;
+ internal short heartbeatPolicy = HeartbeatMode.peer_not_allowed_to_send;
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsTransport.cs b/crypto/src/tls/DtlsTransport.cs
new file mode 100644
index 000000000..a41cb7866
--- /dev/null
+++ b/crypto/src/tls/DtlsTransport.cs
@@ -0,0 +1,139 @@
+using System;
+using System.IO;
+#if !PORTABLE || DOTNET
+using System.Net.Sockets;
+#endif
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DtlsTransport
+ : DatagramTransport
+ {
+ private readonly DtlsRecordLayer m_recordLayer;
+
+ internal DtlsTransport(DtlsRecordLayer recordLayer)
+ {
+ this.m_recordLayer = recordLayer;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual int GetReceiveLimit()
+ {
+ return m_recordLayer.GetReceiveLimit();
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual int GetSendLimit()
+ {
+ return m_recordLayer.GetSendLimit();
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ if (null == buf)
+ throw new ArgumentNullException("buf");
+ if (off < 0 || off >= buf.Length)
+ throw new ArgumentException("invalid offset: " + off, "off");
+ if (len < 0 || len > buf.Length - off)
+ throw new ArgumentException("invalid length: " + len, "len");
+ if (waitMillis < 0)
+ throw new ArgumentException("cannot be negative", "waitMillis");
+
+ try
+ {
+ return m_recordLayer.Receive(buf, off, len, waitMillis);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ m_recordLayer.Fail(fatalAlert.AlertDescription);
+ throw fatalAlert;
+ }
+ catch (TlsTimeoutException e)
+ {
+ throw e;
+ }
+#if !PORTABLE || DOTNET
+ catch (SocketException e)
+ {
+ if (TlsUtilities.IsTimeout(e))
+ throw e;
+
+ m_recordLayer.Fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+#endif
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //catch (InterruptedIOException e)
+ //{
+ // throw e;
+ //}
+ catch (IOException e)
+ {
+ m_recordLayer.Fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ m_recordLayer.Fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void Send(byte[] buf, int off, int len)
+ {
+ if (null == buf)
+ throw new ArgumentNullException("buf");
+ if (off < 0 || off >= buf.Length)
+ throw new ArgumentException("invalid offset: " + off, "off");
+ if (len < 0 || len > buf.Length - off)
+ throw new ArgumentException("invalid length: " + len, "len");
+
+ try
+ {
+ m_recordLayer.Send(buf, off, len);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ m_recordLayer.Fail(fatalAlert.AlertDescription);
+ throw fatalAlert;
+ }
+ catch (TlsTimeoutException e)
+ {
+ throw e;
+ }
+#if !PORTABLE || DOTNET
+ catch (SocketException e)
+ {
+ if (TlsUtilities.IsTimeout(e))
+ throw e;
+
+ m_recordLayer.Fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+#endif
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //catch (InterruptedIOException e)
+ //{
+ // throw e;
+ //}
+ catch (IOException e)
+ {
+ m_recordLayer.Fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ m_recordLayer.Fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void Close()
+ {
+ m_recordLayer.Close();
+ }
+ }
+}
diff --git a/crypto/src/tls/DtlsVerifier.cs b/crypto/src/tls/DtlsVerifier.cs
new file mode 100644
index 000000000..edadeae70
--- /dev/null
+++ b/crypto/src/tls/DtlsVerifier.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class DtlsVerifier
+ {
+ private static TlsMac CreateCookieMac(TlsCrypto crypto)
+ {
+ TlsMac mac = crypto.CreateHmac(MacAlgorithm.hmac_sha256);
+
+ byte[] secret = new byte[mac.MacLength];
+ crypto.SecureRandom.NextBytes(secret);
+
+ mac.SetKey(secret, 0, secret.Length);
+
+ return mac;
+ }
+
+ private readonly TlsMac m_cookieMac;
+ private readonly TlsMacSink m_cookieMacSink;
+
+ public DtlsVerifier(TlsCrypto crypto)
+ {
+ this.m_cookieMac = CreateCookieMac(crypto);
+ this.m_cookieMacSink = new TlsMacSink(m_cookieMac);
+ }
+
+ public virtual DtlsRequest VerifyRequest(byte[] clientID, byte[] data, int dataOff, int dataLen,
+ DatagramSender sender)
+ {
+ lock (this)
+ {
+ bool resetCookieMac = true;
+
+ try
+ {
+ m_cookieMac.Update(clientID, 0, clientID.Length);
+
+ DtlsRequest request = DtlsReliableHandshake.ReadClientRequest(data, dataOff, dataLen,
+ m_cookieMacSink);
+ if (null != request)
+ {
+ byte[] expectedCookie = m_cookieMac.CalculateMac();
+ resetCookieMac = false;
+
+ // TODO Consider stricter HelloVerifyRequest protocol
+ //switch (request.MessageSeq)
+ //{
+ //case 0:
+ //{
+ // DtlsReliableHandshake.SendHelloVerifyRequest(sender, request.RecordSeq, expectedCookie);
+ // break;
+ //}
+ //case 1:
+ //{
+ // if (Arrays.ConstantTimeAreEqual(expectedCookie, request.ClientHello.Cookie))
+ // return request;
+
+ // break;
+ //}
+ //}
+
+ if (Arrays.ConstantTimeAreEqual(expectedCookie, request.ClientHello.Cookie))
+ return request;
+
+ DtlsReliableHandshake.SendHelloVerifyRequest(sender, request.RecordSeq, expectedCookie);
+ }
+ }
+ catch (IOException)
+ {
+ // Ignore
+ }
+ finally
+ {
+ if (resetCookieMac)
+ {
+ m_cookieMac.Reset();
+ }
+ }
+
+ return null;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/ECCurveType.cs b/crypto/src/tls/ECCurveType.cs
new file mode 100644
index 000000000..969d51b98
--- /dev/null
+++ b/crypto/src/tls/ECCurveType.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 4492 5.4</summary>
+ public abstract class ECCurveType
+ {
+ /**
+ * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+ * underlying finite field is a prime field.
+ */
+ public const short explicit_prime = 1;
+
+ /**
+ * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+ * underlying finite field is a characteristic-2 field.
+ */
+ public const short explicit_char2 = 2;
+
+ /**
+ * Indicates that a named curve is used. This option SHOULD be used when applicable.
+ */
+ public const short named_curve = 3;
+
+ /*
+ * Values 248 through 255 are reserved for private use.
+ */
+ }
+}
diff --git a/crypto/src/tls/ECPointFormat.cs b/crypto/src/tls/ECPointFormat.cs
new file mode 100644
index 000000000..d399cc604
--- /dev/null
+++ b/crypto/src/tls/ECPointFormat.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 4492 5.1.2</summary>
+ public abstract class ECPointFormat
+ {
+ public const short uncompressed = 0;
+ public const short ansiX962_compressed_prime = 1;
+ public const short ansiX962_compressed_char2 = 2;
+
+ /*
+ * reserved (248..255)
+ */
+ }
+}
diff --git a/crypto/src/tls/EncryptionAlgorithm.cs b/crypto/src/tls/EncryptionAlgorithm.cs
new file mode 100644
index 000000000..8064451ab
--- /dev/null
+++ b/crypto/src/tls/EncryptionAlgorithm.cs
@@ -0,0 +1,82 @@
+using System;
+
+namespace Org.BouncyCastle.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>
+ public abstract class EncryptionAlgorithm
+ {
+ public const int NULL = 0;
+ public const int RC4_40 = 1;
+ public const int RC4_128 = 2;
+ public const int RC2_CBC_40 = 3;
+ public const int IDEA_CBC = 4;
+ public const int DES40_CBC = 5;
+ public const int DES_CBC = 6;
+ public const int cls_3DES_EDE_CBC = 7;
+
+ /*
+ * RFC 3268
+ */
+ public const int AES_128_CBC = 8;
+ public const int AES_256_CBC = 9;
+
+ /*
+ * RFC 5289
+ */
+ public const int AES_128_GCM = 10;
+ public const int AES_256_GCM = 11;
+
+ /*
+ * RFC 5932
+ */
+ public const int CAMELLIA_128_CBC = 12;
+ public const int CAMELLIA_256_CBC = 13;
+
+ /*
+ * RFC 4162
+ */
+ public const int SEED_CBC = 14;
+
+ /*
+ * RFC 6655
+ */
+ public const int AES_128_CCM = 15;
+ public const int AES_128_CCM_8 = 16;
+ public const int AES_256_CCM = 17;
+ public const int AES_256_CCM_8 = 18;
+
+ /*
+ * RFC 6367
+ */
+ public const int CAMELLIA_128_GCM = 19;
+ public const int CAMELLIA_256_GCM = 20;
+
+ /*
+ * RFC 7905
+ */
+ public const int CHACHA20_POLY1305 = 21;
+
+ /*
+ * RFC 6209
+ */
+ public const int ARIA_128_CBC = 22;
+ public const int ARIA_256_CBC = 23;
+ public const int ARIA_128_GCM = 24;
+ public const int ARIA_256_GCM = 25;
+
+ /*
+ * RFC 8998
+ */
+ public const int SM4_CCM = 26;
+ public const int SM4_GCM = 27;
+
+ /*
+ * GMT 0024-2014
+ */
+ public const int SM4_CBC = 28;
+ }
+}
diff --git a/crypto/src/tls/ExporterLabel.cs b/crypto/src/tls/ExporterLabel.cs
new file mode 100644
index 000000000..481a3eed8
--- /dev/null
+++ b/crypto/src/tls/ExporterLabel.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5705</summary>
+ public abstract class ExporterLabel
+ {
+ /*
+ * RFC 5246
+ */
+ public const string client_finished = "client finished";
+ public const string server_finished = "server finished";
+ public const string master_secret = "master secret";
+ public const string key_expansion = "key expansion";
+
+ /*
+ * RFC 5216
+ */
+ public const string client_EAP_encryption = "client EAP encryption";
+
+ /*
+ * RFC 5281
+ */
+ public const string ttls_keying_material = "ttls keying material";
+ public const string ttls_challenge = "ttls challenge";
+
+ /*
+ * RFC 5764
+ */
+ public const string dtls_srtp = "EXTRACTOR-dtls_srtp";
+
+ /*
+ * RFC 7627
+ */
+ public const string extended_master_secret = "extended master secret";
+
+ /*
+ * draft-ietf-tokbind-protocol-16
+ */
+ public const string token_binding = "EXPORTER-Token-Binding";
+ }
+}
diff --git a/crypto/src/tls/ExtensionType.cs b/crypto/src/tls/ExtensionType.cs
new file mode 100644
index 000000000..87f6a7574
--- /dev/null
+++ b/crypto/src/tls/ExtensionType.cs
@@ -0,0 +1,279 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class ExtensionType
+ {
+ /*
+ * RFC 2546 2.3.
+ */
+ public const int server_name = 0;
+ public const int max_fragment_length = 1;
+ public const int client_certificate_url = 2;
+ public const int trusted_ca_keys = 3;
+ public const int truncated_hmac = 4;
+ public const int status_request = 5;
+
+ /*
+ * RFC 4681
+ */
+ public const int user_mapping = 6;
+
+ /*
+ * RFC 5878
+ */
+ public const int client_authz = 7;
+ public const int server_authz = 8;
+
+ /*
+ * RFC 6091
+ */
+ public const int cert_type = 9;
+
+ /*
+ * RFC 7919 (originally 'elliptic_curves' from RFC 4492)
+ */
+ public const int supported_groups = 10;
+
+ /*
+ * RFC 4492 5.1.
+ */
+ public const int ec_point_formats = 11;
+
+ /*
+ * RFC 5054 2.8.1.
+ */
+ public const int srp = 12;
+
+ /*
+ * RFC 5246 7.4.1.4.
+ */
+ public const int signature_algorithms = 13;
+
+ /*
+ * RFC 5764 9.
+ */
+ public const int use_srtp = 14;
+
+ /*
+ * RFC 6520 6.
+ */
+ public const int heartbeat = 15;
+
+ /*
+ * RFC 7301
+ */
+ public const int application_layer_protocol_negotiation = 16;
+
+ /*
+ * RFC 6961
+ */
+ public const int status_request_v2 = 17;
+
+ /*
+ * RFC 6962
+ */
+ public const int signed_certificate_timestamp = 18;
+
+ /*
+ * RFC 7250
+ */
+ public const int client_certificate_type = 19;
+ public const int server_certificate_type = 20;
+
+ /*
+ * RFC 7685
+ */
+ public const int padding = 21;
+
+ /*
+ * RFC 7366
+ */
+ public const int encrypt_then_mac = 22;
+
+ /*
+ * RFC 7627
+ */
+ public const int extended_master_secret = 23;
+
+ /*
+ * RFC 8472
+ */
+ public const int token_binding = 24;
+
+ /*
+ * RFC 7924
+ */
+ public const int cached_info = 25;
+
+ /*
+ * RFC 8449
+ */
+ public const int record_size_limit = 28;
+
+ /*
+ * RFC 5077 7.
+ */
+ public const int session_ticket = 35;
+
+ /*
+ * RFC 8446
+ */
+ public const int pre_shared_key = 41;
+ public const int early_data = 42;
+ public const int supported_versions = 43;
+ public const int cookie = 44;
+ public const int psk_key_exchange_modes = 45;
+ public const int certificate_authorities = 47;
+ public const int oid_filters = 48;
+ public const int post_handshake_auth = 49;
+ public const int signature_algorithms_cert = 50;
+ public const int key_share = 51;
+
+ /*
+ * RFC 5746 3.2.
+ */
+ public const int renegotiation_info = 0xff01;
+
+ public static string GetName(int extensionType)
+ {
+ switch (extensionType)
+ {
+ case server_name:
+ return "server_name";
+ case max_fragment_length:
+ return "max_fragment_length";
+ case client_certificate_url:
+ return "client_certificate_url";
+ case trusted_ca_keys:
+ return "trusted_ca_keys";
+ case truncated_hmac:
+ return "truncated_hmac";
+ case status_request:
+ return "status_request";
+ case user_mapping:
+ return "user_mapping";
+ case client_authz:
+ return "client_authz";
+ case server_authz:
+ return "server_authz";
+ case cert_type:
+ return "cert_type";
+ case supported_groups:
+ return "supported_groups";
+ case ec_point_formats:
+ return "ec_point_formats";
+ case srp:
+ return "srp";
+ case signature_algorithms:
+ return "signature_algorithms";
+ case use_srtp:
+ return "use_srtp";
+ case heartbeat:
+ return "heartbeat";
+ case application_layer_protocol_negotiation:
+ return "application_layer_protocol_negotiation";
+ case status_request_v2:
+ return "status_request_v2";
+ case signed_certificate_timestamp:
+ return "signed_certificate_timestamp";
+ case client_certificate_type:
+ return "client_certificate_type";
+ case server_certificate_type:
+ return "server_certificate_type";
+ case padding:
+ return "padding";
+ case encrypt_then_mac:
+ return "encrypt_then_mac";
+ case extended_master_secret:
+ return "extended_master_secret";
+ case token_binding:
+ return "token_binding";
+ case cached_info:
+ return "cached_info";
+ case record_size_limit:
+ return "record_size_limit";
+ case session_ticket:
+ return "session_ticket";
+ case pre_shared_key:
+ return "pre_shared_key";
+ case early_data:
+ return "early_data";
+ case supported_versions:
+ return "supported_versions";
+ case cookie:
+ return "cookie";
+ case psk_key_exchange_modes:
+ return "psk_key_exchange_modes";
+ case certificate_authorities:
+ return "certificate_authorities";
+ case oid_filters:
+ return "oid_filters";
+ case post_handshake_auth:
+ return "post_handshake_auth";
+ case signature_algorithms_cert:
+ return "signature_algorithms_cert";
+ case key_share:
+ return "key_share";
+ case renegotiation_info:
+ return "renegotiation_info";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(int extensionType)
+ {
+ return GetName(extensionType) + "(" + extensionType + ")";
+ }
+
+ public static bool IsRecognized(int extensionType)
+ {
+ switch (extensionType)
+ {
+ case server_name:
+ case max_fragment_length:
+ case client_certificate_url:
+ case trusted_ca_keys:
+ case truncated_hmac:
+ case status_request:
+ case user_mapping:
+ case client_authz:
+ case server_authz:
+ case cert_type:
+ case supported_groups:
+ case ec_point_formats:
+ case srp:
+ case signature_algorithms:
+ case use_srtp:
+ case heartbeat:
+ case application_layer_protocol_negotiation:
+ case status_request_v2:
+ case signed_certificate_timestamp:
+ case client_certificate_type:
+ case server_certificate_type:
+ case padding:
+ case encrypt_then_mac:
+ case extended_master_secret:
+ case token_binding:
+ case cached_info:
+ case record_size_limit:
+ case session_ticket:
+ case pre_shared_key:
+ case early_data:
+ case supported_versions:
+ case cookie:
+ case psk_key_exchange_modes:
+ case certificate_authorities:
+ case oid_filters:
+ case post_handshake_auth:
+ case signature_algorithms_cert:
+ case key_share:
+ case renegotiation_info:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/HandshakeMessageInput.cs b/crypto/src/tls/HandshakeMessageInput.cs
new file mode 100644
index 000000000..d7cd19994
--- /dev/null
+++ b/crypto/src/tls/HandshakeMessageInput.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class HandshakeMessageInput
+ : MemoryStream
+ {
+ internal HandshakeMessageInput(byte[] buf, int offset, int length)
+ : base(buf, offset, length, false)
+ {
+ }
+
+ public void UpdateHash(TlsHash hash)
+ {
+ Streams.WriteBufTo(this, new TlsHashSink(hash));
+ }
+ }
+}
diff --git a/crypto/src/tls/HandshakeMessageOutput.cs b/crypto/src/tls/HandshakeMessageOutput.cs
new file mode 100644
index 000000000..ae07b9682
--- /dev/null
+++ b/crypto/src/tls/HandshakeMessageOutput.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal sealed class HandshakeMessageOutput
+ : MemoryStream
+ {
+ internal static int GetLength(int bodyLength)
+ {
+ return 4 + bodyLength;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void Send(TlsProtocol protocol, short handshakeType, byte[] body)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(handshakeType, body.Length);
+ message.Write(body, 0, body.Length);
+ message.Send(protocol);
+ }
+
+ /// <exception cref="IOException"/>
+ internal HandshakeMessageOutput(short handshakeType)
+ : this(handshakeType, 60)
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ internal HandshakeMessageOutput(short handshakeType, int bodyLength)
+ : base(GetLength(bodyLength))
+ {
+ TlsUtilities.CheckUint8(handshakeType);
+ TlsUtilities.WriteUint8(handshakeType, this);
+ // Reserve space for length
+ Seek(3L, SeekOrigin.Current);
+ }
+
+ /// <exception cref="IOException"/>
+ internal void Send(TlsProtocol protocol)
+ {
+ // Patch actual length back in
+ int bodyLength = (int)Length - 4;
+ TlsUtilities.CheckUint24(bodyLength);
+
+ Seek(1L, SeekOrigin.Begin);
+ TlsUtilities.WriteUint24(bodyLength, this);
+
+#if PORTABLE
+ byte[] buf = ToArray();
+ int count = buf.Length;
+#else
+ byte[] buf = GetBuffer();
+ int count = (int)Length;
+#endif
+ protocol.WriteHandshakeMessage(buf, 0, count);
+
+ Platform.Dispose(this);
+ }
+ }
+}
diff --git a/crypto/src/tls/HandshakeType.cs b/crypto/src/tls/HandshakeType.cs
new file mode 100644
index 000000000..563cd1150
--- /dev/null
+++ b/crypto/src/tls/HandshakeType.cs
@@ -0,0 +1,131 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class HandshakeType
+ {
+ /*
+ * RFC 2246 7.4
+ */
+ public const short hello_request = 0;
+ public const short client_hello = 1;
+ public const short server_hello = 2;
+ public const short certificate = 11;
+ public const short server_key_exchange = 12;
+ public const short certificate_request = 13;
+ public const short server_hello_done = 14;
+ public const short certificate_verify = 15;
+ public const short client_key_exchange = 16;
+ public const short finished = 20;
+
+ /*
+ * RFC 3546 2.4
+ */
+ public const short certificate_url = 21;
+ public const short certificate_status = 22;
+
+ /*
+ * (DTLS) RFC 4347 4.3.2
+ */
+ public const short hello_verify_request = 3;
+
+ /*
+ * RFC 4680
+ */
+ public const short supplemental_data = 23;
+
+ /*
+ * RFC 8446
+ */
+ public const short new_session_ticket = 4;
+ public const short end_of_early_data = 5;
+ public const short hello_retry_request = 6;
+ public const short encrypted_extensions = 8;
+ public const short key_update = 24;
+ public const short message_hash = 254;
+
+ public static string GetName(short handshakeType)
+ {
+ switch (handshakeType)
+ {
+ case hello_request:
+ return "hello_request";
+ case client_hello:
+ return "client_hello";
+ case server_hello:
+ return "server_hello";
+ case certificate:
+ return "certificate";
+ case server_key_exchange:
+ return "server_key_exchange";
+ case certificate_request:
+ return "certificate_request";
+ case server_hello_done:
+ return "server_hello_done";
+ case certificate_verify:
+ return "certificate_verify";
+ case client_key_exchange:
+ return "client_key_exchange";
+ case finished:
+ return "finished";
+ case certificate_url:
+ return "certificate_url";
+ case certificate_status:
+ return "certificate_status";
+ case hello_verify_request:
+ return "hello_verify_request";
+ case supplemental_data:
+ return "supplemental_data";
+ case new_session_ticket:
+ return "new_session_ticket";
+ case end_of_early_data:
+ return "end_of_early_data";
+ case hello_retry_request:
+ return "hello_retry_request";
+ case encrypted_extensions:
+ return "encrypted_extensions";
+ case key_update:
+ return "key_update";
+ case message_hash:
+ return "message_hash";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short handshakeType)
+ {
+ return GetName(handshakeType) + "(" + handshakeType + ")";
+ }
+
+ public static bool IsRecognized(short handshakeType)
+ {
+ switch (handshakeType)
+ {
+ case hello_request:
+ case client_hello:
+ case server_hello:
+ case certificate:
+ case server_key_exchange:
+ case certificate_request:
+ case server_hello_done:
+ case certificate_verify:
+ case client_key_exchange:
+ case finished:
+ case certificate_url:
+ case certificate_status:
+ case hello_verify_request:
+ case supplemental_data:
+ case new_session_ticket:
+ case end_of_early_data:
+ case hello_retry_request:
+ case encrypted_extensions:
+ case key_update:
+ case message_hash:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/HashAlgorithm.cs b/crypto/src/tls/HashAlgorithm.cs
new file mode 100644
index 000000000..2c8ba4fff
--- /dev/null
+++ b/crypto/src/tls/HashAlgorithm.cs
@@ -0,0 +1,94 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5246 7.4.1.4.1</summary>
+ public abstract class HashAlgorithm
+ {
+ public const short none = 0;
+ public const short md5 = 1;
+ public const short sha1 = 2;
+ public const short sha224 = 3;
+ public const short sha256 = 4;
+ public const short sha384 = 5;
+ public const short sha512 = 6;
+
+ /*
+ * RFC 8422
+ */
+ public const short Intrinsic = 8;
+
+ public static string GetName(short hashAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case none:
+ return "none";
+ case md5:
+ return "md5";
+ case sha1:
+ return "sha1";
+ case sha224:
+ return "sha224";
+ case sha256:
+ return "sha256";
+ case sha384:
+ return "sha384";
+ case sha512:
+ return "sha512";
+ case Intrinsic:
+ return "Intrinsic";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static int GetOutputSize(short hashAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case md5:
+ return 16;
+ case sha1:
+ return 20;
+ case sha224:
+ return 28;
+ case sha256:
+ return 32;
+ case sha384:
+ return 48;
+ case sha512:
+ return 64;
+ default:
+ return -1;
+ }
+ }
+
+ public static string GetText(short hashAlgorithm)
+ {
+ return GetName(hashAlgorithm) + "(" + hashAlgorithm + ")";
+ }
+
+ public static bool IsPrivate(short hashAlgorithm)
+ {
+ return 224 <= hashAlgorithm && hashAlgorithm <= 255;
+ }
+
+ public static bool IsRecognized(short hashAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case md5:
+ case sha1:
+ case sha224:
+ case sha256:
+ case sha384:
+ case sha512:
+ case Intrinsic:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/HeartbeatExtension.cs b/crypto/src/tls/HeartbeatExtension.cs
new file mode 100644
index 000000000..c44d84a67
--- /dev/null
+++ b/crypto/src/tls/HeartbeatExtension.cs
@@ -0,0 +1,44 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class HeartbeatExtension
+ {
+ private readonly short m_mode;
+
+ public HeartbeatExtension(short mode)
+ {
+ if (!HeartbeatMode.IsValid(mode))
+ throw new ArgumentException("not a valid HeartbeatMode value", "mode");
+
+ this.m_mode = mode;
+ }
+
+ public short Mode
+ {
+ get { return m_mode; }
+ }
+
+ /// <summary>Encode this <see cref="HeartbeatExtension"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_mode, output);
+ }
+
+ /// <summary>Parse a <see cref="HeartbeatExtension"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="HeartbeatExtension"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static HeartbeatExtension Parse(Stream input)
+ {
+ short mode = TlsUtilities.ReadUint8(input);
+ if (!HeartbeatMode.IsValid(mode))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return new HeartbeatExtension(mode);
+ }
+ }
+}
diff --git a/crypto/src/tls/HeartbeatMessage.cs b/crypto/src/tls/HeartbeatMessage.cs
new file mode 100644
index 000000000..9e5c7d14e
--- /dev/null
+++ b/crypto/src/tls/HeartbeatMessage.cs
@@ -0,0 +1,118 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class HeartbeatMessage
+ {
+ public static HeartbeatMessage Create(TlsContext context, short type, byte[] payload)
+ {
+ return Create(context, type, payload, 16);
+ }
+
+ public static HeartbeatMessage Create(TlsContext context, short type, byte[] payload, int paddingLength)
+ {
+ byte[] padding = context.NonceGenerator.GenerateNonce(paddingLength);
+
+ return new HeartbeatMessage(type, payload, padding);
+ }
+
+ private readonly short m_type;
+ private readonly byte[] m_payload;
+ private readonly byte[] m_padding;
+
+ public HeartbeatMessage(short type, byte[] payload, byte[] padding)
+ {
+ if (!HeartbeatMessageType.IsValid(type))
+ throw new ArgumentException("not a valid HeartbeatMessageType value", "type");
+ if (null == payload || payload.Length >= (1 << 16))
+ throw new ArgumentException("must have length < 2^16", "payload");
+ if (null == padding || padding.Length < 16)
+ throw new ArgumentException("must have length >= 16", "padding");
+
+ this.m_type = type;
+ this.m_payload = payload;
+ this.m_padding = padding;
+ }
+
+ public int PaddingLength
+ {
+ /*
+ * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored
+ */
+ get { return m_padding.Length; }
+ }
+
+ public byte[] Payload
+ {
+ get { return m_payload; }
+ }
+
+ public short Type
+ {
+ get { return m_type; }
+ }
+
+ /// <summary>Encode this <see cref="HeartbeatMessage"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_type, output);
+
+ TlsUtilities.CheckUint16(m_payload.Length);
+ TlsUtilities.WriteUint16(m_payload.Length, output);
+ output.Write(m_payload, 0, m_payload.Length);
+
+ output.Write(m_padding, 0, m_padding.Length);
+ }
+
+ /// <summary>Parse a <see cref="HeartbeatMessage"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="HeartbeatMessage"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static HeartbeatMessage Parse(Stream input)
+ {
+ short type = TlsUtilities.ReadUint8(input);
+ if (!HeartbeatMessageType.IsValid(type))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ int payload_length = TlsUtilities.ReadUint16(input);
+ byte[] payloadBuffer = Streams.ReadAll(input);
+
+ byte[] payload = GetPayload(payloadBuffer, payload_length);
+ if (null == payload)
+ {
+ /*
+ * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the received
+ * HeartbeatMessage MUST be discarded silently.
+ */
+ return null;
+ }
+
+ byte[] padding = GetPadding(payloadBuffer, payload_length);
+
+ return new HeartbeatMessage(type, payload, padding);
+ }
+
+ private static byte[] GetPayload(byte[] payloadBuffer, int payloadLength)
+ {
+ /*
+ * RFC 6520 4. The padding_length MUST be at least 16.
+ */
+ int maxPayloadLength = payloadBuffer.Length - 16;
+ if (payloadLength > maxPayloadLength)
+ return null;
+
+ return Arrays.CopyOf(payloadBuffer, payloadLength);
+ }
+
+ private static byte[] GetPadding(byte[] payloadBuffer, int payloadLength)
+ {
+ return TlsUtilities.CopyOfRangeExact(payloadBuffer, payloadLength, payloadBuffer.Length);
+ }
+ }
+}
diff --git a/crypto/src/tls/HeartbeatMessageType.cs b/crypto/src/tls/HeartbeatMessageType.cs
new file mode 100644
index 000000000..18f86b1ab
--- /dev/null
+++ b/crypto/src/tls/HeartbeatMessageType.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 6520 3.</summary>
+ public abstract class HeartbeatMessageType
+ {
+ public const short heartbeat_request = 1;
+ public const short heartbeat_response = 2;
+
+ public static string GetName(short heartbeatMessageType)
+ {
+ switch (heartbeatMessageType)
+ {
+ case heartbeat_request:
+ return "heartbeat_request";
+ case heartbeat_response:
+ return "heartbeat_response";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short heartbeatMessageType)
+ {
+ return GetName(heartbeatMessageType) + "(" + heartbeatMessageType + ")";
+ }
+
+ public static bool IsValid(short heartbeatMessageType)
+ {
+ return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response;
+ }
+ }
+}
diff --git a/crypto/src/tls/HeartbeatMode.cs b/crypto/src/tls/HeartbeatMode.cs
new file mode 100644
index 000000000..8e65d548f
--- /dev/null
+++ b/crypto/src/tls/HeartbeatMode.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /*
+ * RFC 6520
+ */
+ public abstract class HeartbeatMode
+ {
+ public const short peer_allowed_to_send = 1;
+ public const short peer_not_allowed_to_send = 2;
+
+ public static string GetName(short heartbeatMode)
+ {
+ switch (heartbeatMode)
+ {
+ case peer_allowed_to_send:
+ return "peer_allowed_to_send";
+ case peer_not_allowed_to_send:
+ return "peer_not_allowed_to_send";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short heartbeatMode)
+ {
+ return GetName(heartbeatMode) + "(" + heartbeatMode + ")";
+ }
+
+ public static bool IsValid(short heartbeatMode)
+ {
+ return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send;
+ }
+ }
+}
diff --git a/crypto/src/tls/IdentifierType.cs b/crypto/src/tls/IdentifierType.cs
new file mode 100644
index 000000000..df62e82f5
--- /dev/null
+++ b/crypto/src/tls/IdentifierType.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 6066</summary>
+ public abstract class IdentifierType
+ {
+ public const short pre_agreed = 0;
+ public const short key_sha1_hash = 1;
+ public const short x509_name = 2;
+ public const short cert_sha1_hash = 3;
+
+ public static string GetName(short identifierType)
+ {
+ switch (identifierType)
+ {
+ case pre_agreed:
+ return "pre_agreed";
+ case key_sha1_hash:
+ return "key_sha1_hash";
+ case x509_name:
+ return "x509_name";
+ case cert_sha1_hash:
+ return "cert_sha1_hash";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short identifierType)
+ {
+ return GetName(identifierType) + "(" + identifierType + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/KeyExchangeAlgorithm.cs b/crypto/src/tls/KeyExchangeAlgorithm.cs
new file mode 100644
index 000000000..1dfa6db66
--- /dev/null
+++ b/crypto/src/tls/KeyExchangeAlgorithm.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace Org.BouncyCastle.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>
+ public abstract class KeyExchangeAlgorithm
+ {
+ /*
+ * NOTE: We interpret TLS 1.3 cipher suites as having a NULL key exchange
+ */
+ public const int NULL = 0;
+
+ public const int RSA = 1;
+ public const int RSA_EXPORT = 2;
+ public const int DHE_DSS = 3;
+ public const int DHE_DSS_EXPORT = 4;
+ public const int DHE_RSA = 5;
+ public const int DHE_RSA_EXPORT = 6;
+ public const int DH_DSS = 7;
+ public const int DH_DSS_EXPORT = 8;
+ public const int DH_RSA = 9;
+ public const int DH_RSA_EXPORT = 10;
+ public const int DH_anon = 11;
+ public const int DH_anon_EXPORT = 12;
+
+ /*
+ * RFC 4279
+ */
+ public const int PSK = 13;
+ public const int DHE_PSK = 14;
+ public const int RSA_PSK = 15;
+
+ /*
+ * RFC 4429
+ */
+ public const int ECDH_ECDSA = 16;
+ public const int ECDHE_ECDSA = 17;
+ public const int ECDH_RSA = 18;
+ public const int ECDHE_RSA = 19;
+ public const int ECDH_anon = 20;
+
+ /*
+ * RFC 5054
+ */
+ public const int SRP = 21;
+ public const int SRP_DSS = 22;
+ public const int SRP_RSA = 23;
+
+ /*
+ * RFC 5489
+ */
+ public const int ECDHE_PSK = 24;
+
+ /*
+ * GMT 0024-2014
+ */
+ public const int SM2 = 25;
+ }
+}
diff --git a/crypto/src/tls/KeyShareEntry.cs b/crypto/src/tls/KeyShareEntry.cs
new file mode 100644
index 000000000..c4be657c0
--- /dev/null
+++ b/crypto/src/tls/KeyShareEntry.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class KeyShareEntry
+ {
+ private static bool CheckKeyExchangeLength(int length)
+ {
+ return 0 < length && length < (1 << 16);
+ }
+
+ private readonly int m_namedGroup;
+ private readonly byte[] m_keyExchange;
+
+ /// <param name="namedGroup"><see cref="NamedGroup"/></param>
+ /// <param name="keyExchange"></param>
+ public KeyShareEntry(int namedGroup, byte[] keyExchange)
+ {
+ if (!TlsUtilities.IsValidUint16(namedGroup))
+ throw new ArgumentException("should be a uint16", "namedGroup");
+ if (null == keyExchange)
+ throw new ArgumentNullException("keyExchange");
+ if (!CheckKeyExchangeLength(keyExchange.Length))
+ throw new ArgumentException("must have length from 1 to (2^16 - 1)", "keyExchange");
+
+ this.m_namedGroup = namedGroup;
+ this.m_keyExchange = keyExchange;
+ }
+
+ /// <returns><see cref="NamedGroup"/></returns>
+ public int NamedGroup
+ {
+ get { return m_namedGroup; }
+ }
+
+ public byte[] KeyExchange
+ {
+ get { return m_keyExchange; }
+ }
+
+ /// <summary>Encode this <see cref="KeyShareEntry"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint16(NamedGroup, output);
+ TlsUtilities.WriteOpaque16(KeyExchange, output);
+ }
+
+ /// <summary>Parse a <see cref="KeyShareEntry"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="KeyShareEntry"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static KeyShareEntry Parse(Stream input)
+ {
+ int namedGroup = TlsUtilities.ReadUint16(input);
+ byte[] keyExchange = TlsUtilities.ReadOpaque16(input, 1);
+ return new KeyShareEntry(namedGroup, keyExchange);
+ }
+ }
+}
diff --git a/crypto/src/tls/KeyUpdateRequest.cs b/crypto/src/tls/KeyUpdateRequest.cs
new file mode 100644
index 000000000..2a784e6e7
--- /dev/null
+++ b/crypto/src/tls/KeyUpdateRequest.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 8446 4.6.3</summary>
+ public abstract class KeyUpdateRequest
+ {
+ public const short update_not_requested = 0;
+ public const short update_requested = 1;
+
+ public static string GetName(short keyUpdateRequest)
+ {
+ switch (keyUpdateRequest)
+ {
+ case update_not_requested:
+ return "update_not_requested";
+ case update_requested:
+ return "update_requested";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short keyUpdateRequest)
+ {
+ return GetName(keyUpdateRequest) + "(" + keyUpdateRequest + ")";
+ }
+
+ public static bool IsValid(short keyUpdateRequest)
+ {
+ return keyUpdateRequest >= update_not_requested && keyUpdateRequest <= update_requested;
+ }
+ }
+}
diff --git a/crypto/src/tls/MacAlgorithm.cs b/crypto/src/tls/MacAlgorithm.cs
new file mode 100644
index 000000000..1706de1bd
--- /dev/null
+++ b/crypto/src/tls/MacAlgorithm.cs
@@ -0,0 +1,66 @@
+using System;
+
+namespace Org.BouncyCastle.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>
+ public abstract class MacAlgorithm
+ {
+ public const int cls_null = 0;
+ public const int md5 = 1;
+ public const int sha = 2;
+
+ /*
+ * RFC 5246
+ */
+ public const int hmac_md5 = md5;
+ public const int hmac_sha1 = sha;
+ public const int hmac_sha256 = 3;
+ public const int hmac_sha384 = 4;
+ public const int hmac_sha512 = 5;
+
+ public static string GetName(int macAlgorithm)
+ {
+ switch (macAlgorithm)
+ {
+ case cls_null:
+ return "null";
+ case hmac_md5:
+ return "hmac_md5";
+ case hmac_sha1:
+ return "hmac_sha1";
+ case hmac_sha256:
+ return "hmac_sha256";
+ case hmac_sha384:
+ return "hmac_sha384";
+ case hmac_sha512:
+ return "hmac_sha512";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(int macAlgorithm)
+ {
+ return GetName(macAlgorithm) + "(" + macAlgorithm + ")";
+ }
+
+ public static bool IsHmac(int macAlgorithm)
+ {
+ switch (macAlgorithm)
+ {
+ case hmac_md5:
+ case hmac_sha1:
+ case hmac_sha256:
+ case hmac_sha384:
+ case hmac_sha512:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/MaxFragmentLength.cs b/crypto/src/tls/MaxFragmentLength.cs
new file mode 100644
index 000000000..d335de5c5
--- /dev/null
+++ b/crypto/src/tls/MaxFragmentLength.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class MaxFragmentLength
+ {
+ /*
+ * 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 static bool IsValid(short maxFragmentLength)
+ {
+ return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12;
+ }
+ }
+}
diff --git a/crypto/src/tls/NameType.cs b/crypto/src/tls/NameType.cs
new file mode 100644
index 000000000..f2e8ec6b6
--- /dev/null
+++ b/crypto/src/tls/NameType.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class NameType
+ {
+ /*
+ * RFC 3546 3.1.
+ */
+ public const short host_name = 0;
+
+ public static string GetName(short nameType)
+ {
+ switch (nameType)
+ {
+ case host_name:
+ return "host_name";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short nameType)
+ {
+ return GetName(nameType) + "(" + nameType + ")";
+ }
+
+ public static bool IsRecognized(short nameType)
+ {
+ return host_name == nameType;
+ }
+
+ public static bool IsValid(short nameType)
+ {
+ return TlsUtilities.IsValidUint8(nameType);
+ }
+ }
+}
diff --git a/crypto/src/tls/NamedGroup.cs b/crypto/src/tls/NamedGroup.cs
new file mode 100644
index 000000000..0035ef9f3
--- /dev/null
+++ b/crypto/src/tls/NamedGroup.cs
@@ -0,0 +1,416 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 7919</summary>
+ public abstract class NamedGroup
+ {
+ /*
+ * RFC 4492 5.1.1
+ * <p>
+ * The named curves defined here are those specified in SEC 2 [13]. Note that many of these curves
+ * are also recommended in ANSI X9.62 [7] and FIPS 186-2 [11]. Values 0xFE00 through 0xFEFF are
+ * reserved for private use. Values 0xFF01 and 0xFF02 indicate that the client supports arbitrary
+ * prime and characteristic-2 curves, respectively (the curve parameters must be encoded explicitly
+ * in ECParameters).
+ */
+ public const int sect163k1 = 1;
+ public const int sect163r1 = 2;
+ public const int sect163r2 = 3;
+ public const int sect193r1 = 4;
+ public const int sect193r2 = 5;
+ public const int sect233k1 = 6;
+ public const int sect233r1 = 7;
+ public const int sect239k1 = 8;
+ public const int sect283k1 = 9;
+ public const int sect283r1 = 10;
+ public const int sect409k1 = 11;
+ public const int sect409r1 = 12;
+ public const int sect571k1 = 13;
+ public const int sect571r1 = 14;
+ public const int secp160k1 = 15;
+ public const int secp160r1 = 16;
+ public const int secp160r2 = 17;
+ public const int secp192k1 = 18;
+ public const int secp192r1 = 19;
+ public const int secp224k1 = 20;
+ public const int secp224r1 = 21;
+ public const int secp256k1 = 22;
+ public const int secp256r1 = 23;
+ public const int secp384r1 = 24;
+ public const int secp521r1 = 25;
+
+ /*
+ * RFC 7027
+ */
+ public const int brainpoolP256r1 = 26;
+ public const int brainpoolP384r1 = 27;
+ public const int brainpoolP512r1 = 28;
+
+ /*
+ * RFC 8422
+ */
+ public const int x25519 = 29;
+ public const int x448 = 30;
+
+ /*
+ * RFC 8734
+ */
+ public const int brainpoolP256r1tls13 = 31;
+ public const int brainpoolP384r1tls13 = 32;
+ public const int brainpoolP512r1tls13 = 33;
+
+ /*
+ * draft-smyshlyaev-tls12-gost-suites-10
+ */
+ public const int GC256A = 34;
+ public const int GC256B = 35;
+ public const int GC256C = 36;
+ public const int GC256D = 37;
+ public const int GC512A = 38;
+ public const int GC512B = 39;
+ public const int GC512C = 40;
+
+ /*
+ * RFC 8998
+ */
+ public const int curveSM2 = 41;
+
+ /*
+ * RFC 7919 2. Codepoints in the "Supported Groups Registry" with a high byte of 0x01 (that is,
+ * between 256 and 511, inclusive) are set aside for FFDHE groups, though only a small number of
+ * them are initially defined and we do not expect many other FFDHE groups to be added to this
+ * range. No codepoints outside of this range will be allocated to FFDHE groups.
+ */
+ public const int ffdhe2048 = 256;
+ public const int ffdhe3072 = 257;
+ public const int ffdhe4096 = 258;
+ public const int ffdhe6144 = 259;
+ public const int ffdhe8192 = 260;
+
+ /*
+ * RFC 8446 reserved ffdhe_private_use (0x01FC..0x01FF)
+ */
+
+ /*
+ * RFC 4492 reserved ecdhe_private_use (0xFE00..0xFEFF)
+ */
+
+ /*
+ * RFC 4492
+ */
+ public const int arbitrary_explicit_prime_curves = 0xFF01;
+ public const int arbitrary_explicit_char2_curves = 0xFF02;
+
+ /* Names of the actual underlying elliptic curves (not necessarily matching the NamedGroup names). */
+ 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", "X25519", "X448", "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1",
+ "Tc26-Gost-3410-12-256-paramSetA", "GostR3410-2001-CryptoPro-A", "GostR3410-2001-CryptoPro-B",
+ "GostR3410-2001-CryptoPro-C", "Tc26-Gost-3410-12-512-paramSetA", "Tc26-Gost-3410-12-512-paramSetB",
+ "Tc26-Gost-3410-12-512-paramSetC", "sm2p256v1" };
+
+ private static readonly string[] FiniteFieldNames = new string[]{ "ffdhe2048", "ffdhe3072", "ffdhe4096",
+ "ffdhe6144", "ffdhe8192" };
+
+ public static bool CanBeNegotiated(int namedGroup, ProtocolVersion version)
+ {
+ if (TlsUtilities.IsTlsV13(version))
+ {
+ if ((namedGroup >= sect163k1 && namedGroup <= secp256k1)
+ || (namedGroup >= brainpoolP256r1 && namedGroup <= brainpoolP512r1)
+ || (namedGroup >= GC256A && namedGroup <= GC512C)
+ || (namedGroup >= arbitrary_explicit_prime_curves && namedGroup <= arbitrary_explicit_char2_curves))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if ((namedGroup >= brainpoolP256r1tls13 && namedGroup <= brainpoolP512r1tls13)
+ || (namedGroup == curveSM2))
+ {
+ return false;
+ }
+ }
+
+ return IsValid(namedGroup);
+ }
+
+ public static int GetCurveBits(int namedGroup)
+ {
+ switch (namedGroup)
+ {
+ case secp160k1:
+ case secp160r1:
+ case secp160r2:
+ return 160;
+
+ case sect163k1:
+ case sect163r1:
+ case sect163r2:
+ return 163;
+
+ case secp192k1:
+ case secp192r1:
+ return 192;
+
+ case sect193r1:
+ case sect193r2:
+ return 193;
+
+ case secp224k1:
+ case secp224r1:
+ return 224;
+
+ case sect233k1:
+ case sect233r1:
+ return 233;
+
+ case sect239k1:
+ return 239;
+
+ case x25519:
+ return 252;
+
+ case brainpoolP256r1:
+ case brainpoolP256r1tls13:
+ case curveSM2:
+ case GC256A:
+ case GC256B:
+ case GC256C:
+ case GC256D:
+ case secp256k1:
+ case secp256r1:
+ return 256;
+
+ case sect283k1:
+ case sect283r1:
+ return 283;
+
+ case brainpoolP384r1:
+ case brainpoolP384r1tls13:
+ case secp384r1:
+ return 384;
+
+ case sect409k1:
+ case sect409r1:
+ return 409;
+
+ case x448:
+ return 446;
+
+ case brainpoolP512r1:
+ case brainpoolP512r1tls13:
+ case GC512A:
+ case GC512B:
+ case GC512C:
+ return 512;
+
+ case secp521r1:
+ return 521;
+
+ case sect571k1:
+ case sect571r1:
+ return 571;
+
+ default:
+ return 0;
+ }
+ }
+
+ public static string GetCurveName(int namedGroup)
+ {
+ if (RefersToASpecificCurve(namedGroup))
+ {
+ return CurveNames[namedGroup - sect163k1];
+ }
+
+ return null;
+ }
+
+ public static int GetFiniteFieldBits(int namedGroup)
+ {
+ switch (namedGroup)
+ {
+ case ffdhe2048:
+ return 2048;
+ case ffdhe3072:
+ return 3072;
+ case ffdhe4096:
+ return 4096;
+ case ffdhe6144:
+ return 6144;
+ case ffdhe8192:
+ return 8192;
+ default:
+ return 0;
+ }
+ }
+
+ public static string GetFiniteFieldName(int namedGroup)
+ {
+ if (RefersToASpecificFiniteField(namedGroup))
+ {
+ return FiniteFieldNames[namedGroup - ffdhe2048];
+ }
+
+ return null;
+ }
+
+ public static int GetMaximumChar2CurveBits()
+ {
+ return 571;
+ }
+
+ public static int GetMaximumCurveBits()
+ {
+ return 571;
+ }
+
+ public static int GetMaximumFiniteFieldBits()
+ {
+ return 8192;
+ }
+
+ public static int GetMaximumPrimeCurveBits()
+ {
+ return 521;
+ }
+
+ public static string GetName(int namedGroup)
+ {
+ if (IsPrivate(namedGroup))
+ {
+ return "PRIVATE";
+ }
+
+ switch (namedGroup)
+ {
+ case x25519:
+ return "x25519";
+ case x448:
+ return "x448";
+ case brainpoolP256r1tls13:
+ return "brainpoolP256r1tls13";
+ case brainpoolP384r1tls13:
+ return "brainpoolP384r1tls13";
+ case brainpoolP512r1tls13:
+ return "brainpoolP512r1tls13";
+ case GC256A:
+ return "GC256A";
+ case GC256B:
+ return "GC256B";
+ case GC256C:
+ return "GC256C";
+ case GC256D:
+ return "GC256D";
+ case GC512A:
+ return "GC512A";
+ case GC512B:
+ return "GC512B";
+ case GC512C:
+ return "GC512C";
+ case curveSM2:
+ return "curveSM2";
+ case arbitrary_explicit_prime_curves:
+ return "arbitrary_explicit_prime_curves";
+ case arbitrary_explicit_char2_curves:
+ return "arbitrary_explicit_char2_curves";
+ }
+
+ string standardName = GetStandardName(namedGroup);
+ if (null != standardName)
+ {
+ return standardName;
+ }
+
+ return "UNKNOWN";
+ }
+
+ public static string GetStandardName(int namedGroup)
+ {
+ string curveName = GetCurveName(namedGroup);
+ if (null != curveName)
+ {
+ return curveName;
+ }
+
+ string finiteFieldName = GetFiniteFieldName(namedGroup);
+ if (null != finiteFieldName)
+ {
+ return finiteFieldName;
+ }
+
+ return null;
+ }
+
+ public static string GetText(int namedGroup)
+ {
+ return GetName(namedGroup) + "(" + namedGroup + ")";
+ }
+
+ public static bool IsChar2Curve(int namedGroup)
+ {
+ return (namedGroup >= sect163k1 && namedGroup <= sect571r1)
+ || (namedGroup == arbitrary_explicit_char2_curves);
+ }
+
+ public static bool IsPrimeCurve(int namedGroup)
+ {
+ return (namedGroup >= secp160k1 && namedGroup <= curveSM2)
+ || (namedGroup == arbitrary_explicit_prime_curves);
+ }
+
+ public static bool IsPrivate(int namedGroup)
+ {
+ return (namedGroup >> 2) == 0x7F || (namedGroup >> 8) == 0xFE;
+ }
+
+ public static bool IsValid(int namedGroup)
+ {
+ return RefersToASpecificGroup(namedGroup)
+ || IsPrivate(namedGroup)
+ || (namedGroup >= arbitrary_explicit_prime_curves && namedGroup <= arbitrary_explicit_char2_curves);
+ }
+
+ public static bool RefersToAnECDHCurve(int namedGroup)
+ {
+ return RefersToASpecificCurve(namedGroup);
+ }
+
+ public static bool RefersToAnECDSACurve(int namedGroup)
+ {
+ /*
+ * TODO[RFC 8998] Double-check whether this method is only being used to mean
+ * "signature-capable" or specifically ECDSA, and consider curveSM2 behaviour
+ * accordingly.
+ */
+ return RefersToASpecificCurve(namedGroup)
+ && !RefersToAnXDHCurve(namedGroup);
+ }
+
+ public static bool RefersToAnXDHCurve(int namedGroup)
+ {
+ return namedGroup >= x25519 && namedGroup <= x448;
+ }
+
+ public static bool RefersToASpecificCurve(int namedGroup)
+ {
+ return namedGroup >= sect163k1 && namedGroup <= curveSM2;
+ }
+
+ public static bool RefersToASpecificFiniteField(int namedGroup)
+ {
+ return namedGroup >= ffdhe2048 && namedGroup <= ffdhe8192;
+ }
+
+ public static bool RefersToASpecificGroup(int namedGroup)
+ {
+ return RefersToASpecificCurve(namedGroup)
+ || RefersToASpecificFiniteField(namedGroup);
+ }
+ }
+}
diff --git a/crypto/src/tls/NamedGroupRole.cs b/crypto/src/tls/NamedGroupRole.cs
new file mode 100644
index 000000000..d86e6ad61
--- /dev/null
+++ b/crypto/src/tls/NamedGroupRole.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <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>
+ public abstract class NamedGroupRole
+ {
+ public const int dh = 1;
+ public const int ecdh = 2;
+ public const int ecdsa = 3;
+ }
+}
diff --git a/crypto/src/tls/NewSessionTicket.cs b/crypto/src/tls/NewSessionTicket.cs
new file mode 100644
index 000000000..6e2a01305
--- /dev/null
+++ b/crypto/src/tls/NewSessionTicket.cs
@@ -0,0 +1,47 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class NewSessionTicket
+ {
+ private readonly long m_ticketLifetimeHint;
+ private readonly byte[] m_ticket;
+
+ public NewSessionTicket(long ticketLifetimeHint, byte[] ticket)
+ {
+ this.m_ticketLifetimeHint = ticketLifetimeHint;
+ this.m_ticket = ticket;
+ }
+
+ public long TicketLifetimeHint
+ {
+ get { return m_ticketLifetimeHint; }
+ }
+
+ public byte[] Ticket
+ {
+ get { return m_ticket; }
+ }
+
+ /// <summary>Encode this <see cref="NewSessionTicket"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint32(TicketLifetimeHint, output);
+ TlsUtilities.WriteOpaque16(Ticket, output);
+ }
+
+ /// <summary>Parse a <see cref="NewSessionTicket"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="NewSessionTicket"/> object.</returns>
+ /// <exception cref="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/tls/OcspStatusRequest.cs b/crypto/src/tls/OcspStatusRequest.cs
new file mode 100644
index 000000000..b52517e06
--- /dev/null
+++ b/crypto/src/tls/OcspStatusRequest.cs
@@ -0,0 +1,111 @@
+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;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 3546 3.6</summary>
+ public sealed class OcspStatusRequest
+ {
+ private readonly IList m_responderIDList;
+ private readonly X509Extensions m_requestExtensions;
+
+ /// <param name="responderIDList">an <see cref="IList"/> of <see cref="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>
+ /// <param name="requestExtensions">OCSP request extensions. A null value means that there are no extensions.
+ /// </param>
+ public OcspStatusRequest(IList responderIDList, X509Extensions requestExtensions)
+ {
+ this.m_responderIDList = responderIDList;
+ this.m_requestExtensions = requestExtensions;
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="ResponderID"/>.</returns>
+ public IList ResponderIDList
+ {
+ get { return m_responderIDList; }
+ }
+
+ /// <returns>OCSP request extensions.</returns>
+ public X509Extensions RequestExtensions
+ {
+ get { return m_requestExtensions; }
+ }
+
+ /// <summary>Encode this <see cref="OcspStatusRequest"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ if (m_responderIDList == null || m_responderIDList.Count < 1)
+ {
+ TlsUtilities.WriteUint16(0, output);
+ }
+ else
+ {
+ MemoryStream buf = new MemoryStream();
+ foreach (ResponderID responderID in m_responderIDList)
+ {
+ byte[] derEncoding = responderID.GetEncoded(Asn1Encodable.Der);
+ TlsUtilities.WriteOpaque16(derEncoding, buf);
+ }
+ TlsUtilities.CheckUint16(buf.Length);
+ TlsUtilities.WriteUint16((int)buf.Length, output);
+ Streams.WriteBufTo(buf, output);
+ }
+
+ if (m_requestExtensions == null)
+ {
+ TlsUtilities.WriteUint16(0, output);
+ }
+ else
+ {
+ byte[] derEncoding = m_requestExtensions.GetEncoded(Asn1Encodable.Der);
+ TlsUtilities.CheckUint16(derEncoding.Length);
+ TlsUtilities.WriteUint16(derEncoding.Length, output);
+ output.Write(derEncoding, 0, derEncoding.Length);
+ }
+ }
+
+ /// <summary>Parse an <see cref="OcspStatusRequest"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>an <see cref="OcspStatusRequest"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static OcspStatusRequest Parse(Stream input)
+ {
+ IList responderIDList = Platform.CreateArrayList();
+ {
+ byte[] data = TlsUtilities.ReadOpaque16(input);
+ if (data.Length > 0)
+ {
+ MemoryStream buf = new MemoryStream(data, false);
+ do
+ {
+ byte[] derEncoding = TlsUtilities.ReadOpaque16(buf, 1);
+ ResponderID responderID = ResponderID.GetInstance(TlsUtilities.ReadDerObject(derEncoding));
+ responderIDList.Add(responderID);
+ }
+ while (buf.Position < buf.Length);
+ }
+ }
+
+ X509Extensions requestExtensions = null;
+ {
+ byte[] derEncoding = TlsUtilities.ReadOpaque16(input);
+ if (derEncoding.Length > 0)
+ {
+ requestExtensions = X509Extensions.GetInstance(TlsUtilities.ReadDerObject(derEncoding));
+ }
+ }
+
+ return new OcspStatusRequest(responderIDList, requestExtensions);
+ }
+ }
+}
diff --git a/crypto/src/tls/OfferedPsks.cs b/crypto/src/tls/OfferedPsks.cs
new file mode 100644
index 000000000..597ec195c
--- /dev/null
+++ b/crypto/src/tls/OfferedPsks.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class OfferedPsks
+ {
+ private readonly IList m_identities;
+ private readonly IList m_binders;
+
+ public OfferedPsks(IList identities, IList binders)
+ {
+ if (null == identities || identities.Count < 1)
+ throw new ArgumentException("cannot be null or empty", "identities");
+ if (null == binders || identities.Count != binders.Count)
+ throw new ArgumentException("must be non-null and the same length as 'identities'", "binders");
+
+ this.m_identities = identities;
+ this.m_binders = binders;
+ }
+
+ public IList Binders
+ {
+ get { return m_binders; }
+ }
+
+ public IList Identities
+ {
+ get { return m_identities; }
+ }
+
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ // identities
+ {
+ int totalLengthIdentities = 0;
+ foreach (PskIdentity identity in m_identities)
+ {
+ totalLengthIdentities += 2 + identity.Identity.Length + 4;
+ }
+
+ TlsUtilities.CheckUint16(totalLengthIdentities);
+ TlsUtilities.WriteUint16(totalLengthIdentities, output);
+
+ foreach (PskIdentity identity in m_identities)
+ {
+ identity.Encode(output);
+ }
+ }
+
+ // binders
+ {
+ int totalLengthBinders = 0;
+ foreach (byte[] binder in m_binders)
+ {
+ totalLengthBinders += 1 + binder.Length;
+ }
+
+ TlsUtilities.CheckUint16(totalLengthBinders);
+ TlsUtilities.WriteUint16(totalLengthBinders, output);
+
+ foreach (byte[] binder in m_binders)
+ {
+ TlsUtilities.WriteOpaque8(binder, output);
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public static OfferedPsks Parse(Stream input)
+ {
+ IList identities = Platform.CreateArrayList();
+ {
+ int totalLengthIdentities = TlsUtilities.ReadUint16(input);
+ if (totalLengthIdentities < 7)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] identitiesData = TlsUtilities.ReadFully(totalLengthIdentities, input);
+ MemoryStream buf = new MemoryStream(identitiesData, false);
+ do
+ {
+ PskIdentity identity = PskIdentity.Parse(buf);
+ identities.Add(identity);
+ }
+ while (buf.Position < buf.Length);
+ }
+
+ IList binders = Platform.CreateArrayList();
+ {
+ int totalLengthBinders = TlsUtilities.ReadUint16(input);
+ if (totalLengthBinders < 33)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] bindersData = TlsUtilities.ReadFully(totalLengthBinders, input);
+ MemoryStream buf = new MemoryStream(bindersData, false);
+ do
+ {
+ byte[] binder = TlsUtilities.ReadOpaque8(input, 32);
+ binders.Add(binder);
+ }
+ while (buf.Position < buf.Length);
+ }
+
+ return new OfferedPsks(identities, binders);
+ }
+ }
+}
diff --git a/crypto/src/tls/PrfAlgorithm.cs b/crypto/src/tls/PrfAlgorithm.cs
new file mode 100644
index 000000000..ec9c2f249
--- /dev/null
+++ b/crypto/src/tls/PrfAlgorithm.cs
@@ -0,0 +1,49 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5246</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>
+ public abstract class PrfAlgorithm
+ {
+ public const int ssl_prf_legacy = 0;
+ public const int tls_prf_legacy = 1;
+ public const int tls_prf_sha256 = 2;
+ public const int tls_prf_sha384 = 3;
+ public const int tls13_hkdf_sha256 = 4;
+ public const int tls13_hkdf_sha384 = 5;
+ //public const int tls13_hkdf_sha512 = 6;
+ public const int tls13_hkdf_sm3 = 7;
+
+ public static string GetName(int prfAlgorithm)
+ {
+ switch (prfAlgorithm)
+ {
+ case ssl_prf_legacy:
+ return "ssl_prf_legacy";
+ case tls_prf_legacy:
+ return "tls_prf_legacy";
+ case tls_prf_sha256:
+ return "tls_prf_sha256";
+ case tls_prf_sha384:
+ return "tls_prf_sha384";
+ case tls13_hkdf_sha256:
+ return "tls13_hkdf_sha256";
+ case tls13_hkdf_sha384:
+ return "tls13_hkdf_sha384";
+ case tls13_hkdf_sm3:
+ return "tls13_hkdf_sm3";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(int prfAlgorithm)
+ {
+ return GetName(prfAlgorithm) + "(" + prfAlgorithm + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/ProtocolName.cs b/crypto/src/tls/ProtocolName.cs
new file mode 100644
index 000000000..529e81b19
--- /dev/null
+++ b/crypto/src/tls/ProtocolName.cs
@@ -0,0 +1,88 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 7301 Represents a protocol name for use with ALPN.</summary>
+ public sealed class ProtocolName
+ {
+ public static ProtocolName AsRawBytes(byte[] bytes)
+ {
+ return new ProtocolName(Arrays.Clone(bytes));
+ }
+
+ public static ProtocolName AsUtf8Encoding(String name)
+ {
+ return new ProtocolName(Strings.ToUtf8ByteArray(name));
+ }
+
+ public static readonly ProtocolName Http_1_1 = AsUtf8Encoding("http/1.1");
+ public static readonly ProtocolName Spdy_1 = AsUtf8Encoding("spdy/1");
+ public static readonly ProtocolName Spdy_2 = AsUtf8Encoding("spdy/2");
+ public static readonly ProtocolName Spdy_3 = AsUtf8Encoding("spdy/3");
+ public static readonly ProtocolName Stun_Turn = AsUtf8Encoding("stun.turn");
+ public static readonly ProtocolName Stun_Nat_Discovery = AsUtf8Encoding("stun.nat-discovery");
+ public static readonly ProtocolName Http_2_Tls = AsUtf8Encoding("h2");
+ public static readonly ProtocolName Http_2_Tcp = AsUtf8Encoding("h2c");
+ public static readonly ProtocolName WebRtc = AsUtf8Encoding("webrtc");
+ public static readonly ProtocolName WebRtc_Confidential = AsUtf8Encoding("c-webrtc");
+ public static readonly ProtocolName Ftp = AsUtf8Encoding("ftp");
+ public static readonly ProtocolName Imap = AsUtf8Encoding("imap");
+ public static readonly ProtocolName Pop3 = AsUtf8Encoding("pop3");
+ public static readonly ProtocolName ManageSieve = AsUtf8Encoding("managesieve");
+ public static readonly ProtocolName Coap = AsUtf8Encoding("coap");
+ public static readonly ProtocolName Xmpp_Client = AsUtf8Encoding("xmpp-client");
+ public static readonly ProtocolName Xmpp_Server = AsUtf8Encoding("xmpp-server");
+
+ private readonly byte[] m_bytes;
+
+ private ProtocolName(byte[] bytes)
+ {
+ if (bytes == null)
+ throw new ArgumentNullException("bytes");
+ if (bytes.Length < 1 || bytes.Length > 255)
+ throw new ArgumentException("must have length from 1 to 255", "bytes");
+
+ this.m_bytes = bytes;
+ }
+
+ public byte[] GetBytes()
+ {
+ return Arrays.Clone(m_bytes);
+ }
+
+ public string GetUtf8Decoding()
+ {
+ return Strings.FromUtf8ByteArray(m_bytes);
+ }
+
+ /// <summary>Encode this <see cref="ProtocolName"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteOpaque8(m_bytes, output);
+ }
+
+ /// <summary>Parse a <see cref="ProtocolName"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="ProtocolName"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static ProtocolName Parse(Stream input)
+ {
+ return new ProtocolName(TlsUtilities.ReadOpaque8(input, 1));
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ProtocolName && Arrays.AreEqual(m_bytes, ((ProtocolName)obj).m_bytes);
+ }
+
+ public override int GetHashCode()
+ {
+ return Arrays.GetHashCode(m_bytes);
+ }
+ }
+}
diff --git a/crypto/src/tls/ProtocolVersion.cs b/crypto/src/tls/ProtocolVersion.cs
new file mode 100644
index 000000000..f37ce382d
--- /dev/null
+++ b/crypto/src/tls/ProtocolVersion.cs
@@ -0,0 +1,406 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class ProtocolVersion
+ {
+ public static readonly ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0");
+ public static readonly ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0");
+ public static readonly ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1");
+ public static readonly ProtocolVersion TLSv12 = new ProtocolVersion(0x0303, "TLS 1.2");
+ public static readonly ProtocolVersion TLSv13 = new ProtocolVersion(0x0304, "TLS 1.3");
+ public static readonly ProtocolVersion DTLSv10 = new ProtocolVersion(0xFEFF, "DTLS 1.0");
+ public static readonly ProtocolVersion DTLSv12 = new ProtocolVersion(0xFEFD, "DTLS 1.2");
+
+ internal static readonly ProtocolVersion CLIENT_EARLIEST_SUPPORTED_DTLS = DTLSv10;
+ internal static readonly ProtocolVersion CLIENT_EARLIEST_SUPPORTED_TLS = SSLv3;
+ internal static readonly ProtocolVersion CLIENT_LATEST_SUPPORTED_DTLS = DTLSv12;
+ internal static readonly ProtocolVersion CLIENT_LATEST_SUPPORTED_TLS = TLSv13;
+
+ internal static readonly ProtocolVersion SERVER_EARLIEST_SUPPORTED_DTLS = DTLSv10;
+ internal static readonly ProtocolVersion SERVER_EARLIEST_SUPPORTED_TLS = SSLv3;
+ internal static readonly ProtocolVersion SERVER_LATEST_SUPPORTED_DTLS = DTLSv12;
+ internal static readonly ProtocolVersion SERVER_LATEST_SUPPORTED_TLS = TLSv13;
+
+ public static bool Contains(ProtocolVersion[] versions, ProtocolVersion version)
+ {
+ if (versions != null && version != null)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ if (version.Equals(versions[i]))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static ProtocolVersion GetEarliestDtls(ProtocolVersion[] versions)
+ {
+ ProtocolVersion earliest = null;
+ if (null != versions)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ ProtocolVersion next = versions[i];
+ if (null != next && next.IsDtls)
+ {
+ if (null == earliest || next.MinorVersion > earliest.MinorVersion)
+ {
+ earliest = next;
+ }
+ }
+ }
+ }
+ return earliest;
+ }
+
+ public static ProtocolVersion GetEarliestTls(ProtocolVersion[] versions)
+ {
+ ProtocolVersion earliest = null;
+ if (null != versions)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ ProtocolVersion next = versions[i];
+ if (null != next && next.IsTls)
+ {
+ if (null == earliest || next.MinorVersion < earliest.MinorVersion)
+ {
+ earliest = next;
+ }
+ }
+ }
+ }
+ return earliest;
+ }
+
+ public static ProtocolVersion GetLatestDtls(ProtocolVersion[] versions)
+ {
+ ProtocolVersion latest = null;
+ if (null != versions)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ ProtocolVersion next = versions[i];
+ if (null != next && next.IsDtls)
+ {
+ if (null == latest || next.MinorVersion < latest.MinorVersion)
+ {
+ latest = next;
+ }
+ }
+ }
+ }
+ return latest;
+ }
+
+ public static ProtocolVersion GetLatestTls(ProtocolVersion[] versions)
+ {
+ ProtocolVersion latest = null;
+ if (null != versions)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ ProtocolVersion next = versions[i];
+ if (null != next && next.IsTls)
+ {
+ if (null == latest || next.MinorVersion > latest.MinorVersion)
+ {
+ latest = next;
+ }
+ }
+ }
+ }
+ return latest;
+ }
+
+ internal static bool IsSupportedDtlsVersionClient(ProtocolVersion version)
+ {
+ return null != version
+ && version.IsEqualOrLaterVersionOf(CLIENT_EARLIEST_SUPPORTED_DTLS)
+ && version.IsEqualOrEarlierVersionOf(CLIENT_LATEST_SUPPORTED_DTLS);
+ }
+
+ internal static bool IsSupportedDtlsVersionServer(ProtocolVersion version)
+ {
+ return null != version
+ && version.IsEqualOrLaterVersionOf(SERVER_EARLIEST_SUPPORTED_DTLS)
+ && version.IsEqualOrEarlierVersionOf(SERVER_LATEST_SUPPORTED_DTLS);
+ }
+
+ internal static bool IsSupportedTlsVersionClient(ProtocolVersion version)
+ {
+ if (null == version)
+ return false;
+
+ int fullVersion = version.FullVersion;
+
+ return fullVersion >= CLIENT_EARLIEST_SUPPORTED_TLS.FullVersion
+ && fullVersion <= CLIENT_LATEST_SUPPORTED_TLS.FullVersion;
+ }
+
+ internal static bool IsSupportedTlsVersionServer(ProtocolVersion version)
+ {
+ if (null == version)
+ return false;
+
+ int fullVersion = version.FullVersion;
+
+ return fullVersion >= SERVER_EARLIEST_SUPPORTED_TLS.FullVersion
+ && fullVersion <= SERVER_LATEST_SUPPORTED_TLS.FullVersion;
+ }
+
+ private readonly int version;
+ private readonly string name;
+
+ private ProtocolVersion(int v, string name)
+ {
+ this.version = v & 0xFFFF;
+ this.name = name;
+ }
+
+ public ProtocolVersion[] DownTo(ProtocolVersion min)
+ {
+ if (!IsEqualOrLaterVersionOf(min))
+ throw new ArgumentException("must be an equal or earlier version of this one", "min");
+
+ IList result = Platform.CreateArrayList();
+ result.Add(this);
+
+ ProtocolVersion current = this;
+ while (!current.Equals(min))
+ {
+ current = current.GetPreviousVersion();
+ result.Add(current);
+ }
+
+ ProtocolVersion[] versions = new ProtocolVersion[result.Count];
+ for (int i = 0; i < result.Count; ++i)
+ {
+ versions[i] = (ProtocolVersion)result[i];
+ }
+ return versions;
+ }
+
+ public int FullVersion
+ {
+ get { return version; }
+ }
+
+ public int MajorVersion
+ {
+ get { return version >> 8; }
+ }
+
+ public int MinorVersion
+ {
+ get { return version & 0xFF; }
+ }
+
+ public string Name
+ {
+ get { return name; }
+ }
+
+ public bool IsDtls
+ {
+ get { return MajorVersion == 0xFE; }
+ }
+
+ public bool IsSsl
+ {
+ get { return this == SSLv3; }
+ }
+
+ public bool IsTls
+ {
+ get { return MajorVersion == 0x03; }
+ }
+
+ public ProtocolVersion GetEquivalentTlsVersion()
+ {
+ switch (MajorVersion)
+ {
+ case 0x03:
+ return this;
+ case 0xFE:
+ switch (MinorVersion)
+ {
+ case 0xFF: return TLSv11;
+ case 0xFD: return TLSv12;
+ default: return null;
+ }
+ default:
+ return null;
+ }
+ }
+
+ public ProtocolVersion GetNextVersion()
+ {
+ int major = MajorVersion, minor = MinorVersion;
+ switch (major)
+ {
+ case 0x03:
+ switch (minor)
+ {
+ case 0xFF: return null;
+ default: return Get(major, minor + 1);
+ }
+ case 0xFE:
+ switch (minor)
+ {
+ case 0x00: return null;
+ case 0xFF: return DTLSv12;
+ default: return Get(major, minor - 1);
+ }
+ default:
+ return null;
+ }
+ }
+
+ public ProtocolVersion GetPreviousVersion()
+ {
+ int major = MajorVersion, minor = MinorVersion;
+ switch (major)
+ {
+ case 0x03:
+ switch (minor)
+ {
+ case 0x00: return null;
+ default: return Get(major, minor - 1);
+ }
+ case 0xFE:
+ switch (minor)
+ {
+ case 0xFF: return null;
+ case 0xFD: return DTLSv10;
+ default: return Get(major, minor + 1);
+ }
+ default:
+ return null;
+ }
+ }
+
+ public bool IsEarlierVersionOf(ProtocolVersion version)
+ {
+ if (null == version || MajorVersion != version.MajorVersion)
+ return false;
+
+ int diffMinorVersion = MinorVersion - version.MinorVersion;
+ return IsDtls ? diffMinorVersion > 0 : diffMinorVersion < 0;
+ }
+
+ public bool IsEqualOrEarlierVersionOf(ProtocolVersion version)
+ {
+ if (null == version || MajorVersion != version.MajorVersion)
+ return false;
+
+ int diffMinorVersion = MinorVersion - version.MinorVersion;
+ return IsDtls ? diffMinorVersion >= 0 : diffMinorVersion <= 0;
+ }
+
+ public bool IsEqualOrLaterVersionOf(ProtocolVersion version)
+ {
+ if (null == version || MajorVersion != version.MajorVersion)
+ return false;
+
+ int diffMinorVersion = MinorVersion - version.MinorVersion;
+ return IsDtls ? diffMinorVersion <= 0 : diffMinorVersion >= 0;
+ }
+
+ public bool IsLaterVersionOf(ProtocolVersion version)
+ {
+ if (null == version || MajorVersion != version.MajorVersion)
+ return false;
+
+ int diffMinorVersion = MinorVersion - version.MinorVersion;
+ return IsDtls ? diffMinorVersion < 0 : diffMinorVersion > 0;
+ }
+
+ public override bool Equals(object other)
+ {
+ return this == other || (other is ProtocolVersion && Equals((ProtocolVersion)other));
+ }
+
+ public bool Equals(ProtocolVersion other)
+ {
+ return other != null && this.version == other.version;
+ }
+
+ public override int GetHashCode()
+ {
+ return version;
+ }
+
+ public static ProtocolVersion Get(int major, int minor)
+ {
+ switch (major)
+ {
+ case 0x03:
+ {
+ switch (minor)
+ {
+ case 0x00:
+ return SSLv3;
+ case 0x01:
+ return TLSv10;
+ case 0x02:
+ return TLSv11;
+ case 0x03:
+ return TLSv12;
+ case 0x04:
+ return TLSv13;
+ }
+ return GetUnknownVersion(major, minor, "TLS");
+ }
+ case 0xFE:
+ {
+ switch (minor)
+ {
+ case 0xFF:
+ return DTLSv10;
+ case 0xFE:
+ throw new ArgumentException("{0xFE, 0xFE} is a reserved protocol version");
+ case 0xFD:
+ return DTLSv12;
+ }
+ return GetUnknownVersion(major, minor, "DTLS");
+ }
+ default:
+ {
+ return GetUnknownVersion(major, minor, "UNKNOWN");
+ }
+ }
+ }
+
+ public ProtocolVersion[] Only()
+ {
+ return new ProtocolVersion[]{ this };
+ }
+
+ public override string ToString()
+ {
+ return name;
+ }
+
+ private static void CheckUint8(int versionOctet)
+ {
+ if (!TlsUtilities.IsValidUint8(versionOctet))
+ throw new ArgumentException("not a valid octet", "versionOctet");
+ }
+
+ private static ProtocolVersion GetUnknownVersion(int major, int minor, string prefix)
+ {
+ CheckUint8(major);
+ CheckUint8(minor);
+
+ int v = (major << 8) | minor;
+ string hex = Platform.ToUpperInvariant(Convert.ToString(0x10000 | v, 16).Substring(1));
+ return new ProtocolVersion(v, prefix + " 0x" + hex);
+ }
+ }
+}
diff --git a/crypto/src/tls/PskIdentity.cs b/crypto/src/tls/PskIdentity.cs
new file mode 100644
index 000000000..9b24527bb
--- /dev/null
+++ b/crypto/src/tls/PskIdentity.cs
@@ -0,0 +1,47 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class PskIdentity
+ {
+ private readonly byte[] m_identity;
+ private readonly long m_obfuscatedTicketAge;
+
+ public PskIdentity(byte[] identity, long obfuscatedTicketAge)
+ {
+ if (null == identity)
+ throw new ArgumentNullException("identity");
+ if (identity.Length < 1 || !TlsUtilities.IsValidUint16(identity.Length))
+ throw new ArgumentException("should have length from 1 to 65535", "identity");
+ if (!TlsUtilities.IsValidUint32(obfuscatedTicketAge))
+ throw new ArgumentException("should be a uint32", "obfuscatedTicketAge");
+
+ this.m_identity = identity;
+ this.m_obfuscatedTicketAge = obfuscatedTicketAge;
+ }
+
+ public byte[] Identity
+ {
+ get { return m_identity; }
+ }
+
+ public long ObfuscatedTicketAge
+ {
+ get { return m_obfuscatedTicketAge; }
+ }
+
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteOpaque16(Identity, output);
+ TlsUtilities.WriteUint32(ObfuscatedTicketAge, output);
+ }
+
+ public static PskIdentity Parse(Stream input)
+ {
+ byte[] identity = TlsUtilities.ReadOpaque16(input, 1);
+ long obfuscatedTicketAge = TlsUtilities.ReadUint32(input);
+ return new PskIdentity(identity, obfuscatedTicketAge);
+ }
+ }
+}
diff --git a/crypto/src/tls/PskKeyExchangeMode.cs b/crypto/src/tls/PskKeyExchangeMode.cs
new file mode 100644
index 000000000..565745b67
--- /dev/null
+++ b/crypto/src/tls/PskKeyExchangeMode.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class PskKeyExchangeMode
+ {
+ /*
+ * RFC 8446
+ */
+
+ public const short psk_ke = 0;
+ public const short psk_dhe_ke = 1;
+
+ public static string GetName(short pskKeyExchangeMode)
+ {
+ switch (pskKeyExchangeMode)
+ {
+ case psk_ke:
+ return "psk_ke";
+ case psk_dhe_ke:
+ return "psk_dhe_ke";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short pskKeyExchangeMode)
+ {
+ return GetName(pskKeyExchangeMode) + "(" + pskKeyExchangeMode + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/PskTlsClient.cs b/crypto/src/tls/PskTlsClient.cs
new file mode 100644
index 000000000..3e9a00390
--- /dev/null
+++ b/crypto/src/tls/PskTlsClient.cs
@@ -0,0 +1,60 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class PskTlsClient
+ : AbstractTlsClient
+ {
+ private static readonly int[] DefaultCipherSuites = new int[]
+ {
+ CipherSuite.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+ };
+
+ protected readonly TlsPskIdentity m_pskIdentity;
+
+ public PskTlsClient(TlsCrypto crypto, byte[] identity, byte[] psk)
+ : this(crypto, new BasicTlsPskIdentity(identity, psk))
+ {
+ }
+
+ public PskTlsClient(TlsCrypto crypto, TlsPskIdentity pskIdentity)
+ : base(crypto)
+ {
+ this.m_pskIdentity = pskIdentity;
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.DownTo(ProtocolVersion.TLSv10);
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
+ }
+
+ public override TlsPskIdentity GetPskIdentity()
+ {
+ return m_pskIdentity;
+ }
+
+ /// <exception cref="IOException"/>
+ public override TlsAuthentication GetAuthentication()
+ {
+ /*
+ * 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);
+ }
+ }
+}
diff --git a/crypto/src/tls/PskTlsServer.cs b/crypto/src/tls/PskTlsServer.cs
new file mode 100644
index 000000000..7197b6ad8
--- /dev/null
+++ b/crypto/src/tls/PskTlsServer.cs
@@ -0,0 +1,76 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class PskTlsServer
+ : AbstractTlsServer
+ {
+ private static readonly int[] DefaultCipherSuites = new int[]
+ {
+ CipherSuite.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+ };
+
+ protected readonly TlsPskIdentityManager m_pskIdentityManager;
+
+ public PskTlsServer(TlsCrypto crypto, TlsPskIdentityManager pskIdentityManager)
+ : base(crypto)
+ {
+ this.m_pskIdentityManager = pskIdentityManager;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.DownTo(ProtocolVersion.TLSv10);
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ int keyExchangeAlgorithm = m_context.SecurityParameters.KeyExchangeAlgorithm;
+
+ 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 TlsPskIdentityManager GetPskIdentityManager()
+ {
+ return m_pskIdentityManager;
+ }
+ }
+}
diff --git a/crypto/src/tls/RecordFormat.cs b/crypto/src/tls/RecordFormat.cs
new file mode 100644
index 000000000..3bc046902
--- /dev/null
+++ b/crypto/src/tls/RecordFormat.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class RecordFormat
+ {
+ public const int TypeOffset = 0;
+ public const int VersionOffset = 1;
+ public const int LengthOffset = 3;
+ public const int FragmentOffset = 5;
+ }
+}
diff --git a/crypto/src/tls/RecordPreview.cs b/crypto/src/tls/RecordPreview.cs
new file mode 100644
index 000000000..a0abb0fe3
--- /dev/null
+++ b/crypto/src/tls/RecordPreview.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class RecordPreview
+ {
+ private readonly int recordSize;
+ private readonly int contentLimit;
+
+ internal static RecordPreview CombineAppData(RecordPreview a, RecordPreview b)
+ {
+ return new RecordPreview(a.RecordSize + b.RecordSize, a.ContentLimit + b.ContentLimit);
+ }
+
+ internal static RecordPreview ExtendRecordSize(RecordPreview a, int recordSize)
+ {
+ return new RecordPreview(a.RecordSize + recordSize, a.ContentLimit);
+ }
+
+ internal RecordPreview(int recordSize, int contentLimit)
+ {
+ this.recordSize = recordSize;
+ this.contentLimit = contentLimit;
+ }
+
+ public int ContentLimit
+ {
+ get { return contentLimit; }
+ }
+
+ public int RecordSize
+ {
+ get { return recordSize; }
+ }
+ }
+}
diff --git a/crypto/src/tls/RecordStream.cs b/crypto/src/tls/RecordStream.cs
new file mode 100644
index 000000000..a97d34698
--- /dev/null
+++ b/crypto/src/tls/RecordStream.cs
@@ -0,0 +1,533 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>An implementation of the TLS 1.0/1.1/1.2 record layer.</summary>
+ internal sealed class RecordStream
+ {
+ private const int DefaultPlaintextLimit = (1 << 14);
+
+ private readonly Record m_inputRecord = new Record();
+ private readonly SequenceNumber m_readSeqNo = new SequenceNumber(), m_writeSeqNo = new SequenceNumber();
+
+ private readonly TlsProtocol m_handler;
+ private readonly Stream m_input;
+ private readonly Stream m_output;
+
+ private TlsCipher m_pendingCipher = null;
+ private TlsCipher m_readCipher = TlsNullNullCipher.Instance;
+ private TlsCipher m_readCipherDeferred = null;
+ private TlsCipher m_writeCipher = TlsNullNullCipher.Instance;
+
+ private ProtocolVersion m_writeVersion = null;
+
+ private int m_plaintextLimit = DefaultPlaintextLimit;
+ private int m_ciphertextLimit = DefaultPlaintextLimit;
+ private bool m_ignoreChangeCipherSpec = false;
+
+ internal RecordStream(TlsProtocol handler, Stream input, Stream output)
+ {
+ this.m_handler = handler;
+ this.m_input = input;
+ this.m_output = output;
+ }
+
+ internal int PlaintextLimit
+ {
+ get { return m_plaintextLimit; }
+ }
+
+ internal void SetPlaintextLimit(int plaintextLimit)
+ {
+ this.m_plaintextLimit = plaintextLimit;
+ this.m_ciphertextLimit = m_readCipher.GetCiphertextDecodeLimit(plaintextLimit);
+ }
+
+ internal void SetWriteVersion(ProtocolVersion writeVersion)
+ {
+ this.m_writeVersion = writeVersion;
+ }
+
+ internal void SetIgnoreChangeCipherSpec(bool ignoreChangeCipherSpec)
+ {
+ this.m_ignoreChangeCipherSpec = ignoreChangeCipherSpec;
+ }
+
+ internal void SetPendingCipher(TlsCipher tlsCipher)
+ {
+ this.m_pendingCipher = tlsCipher;
+ }
+
+ /// <exception cref="IOException"/>
+ internal void NotifyChangeCipherSpecReceived()
+ {
+ if (m_pendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message, "No pending cipher");
+
+ EnablePendingCipherRead(false);
+ }
+
+ /// <exception cref="IOException"/>
+ internal void EnablePendingCipherRead(bool deferred)
+ {
+ if (m_pendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (m_readCipherDeferred != null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (deferred)
+ {
+ this.m_readCipherDeferred = m_pendingCipher;
+ }
+ else
+ {
+ this.m_readCipher = m_pendingCipher;
+ this.m_ciphertextLimit = m_readCipher.GetCiphertextDecodeLimit(m_plaintextLimit);
+ m_readSeqNo.Reset();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal void EnablePendingCipherWrite()
+ {
+ if (m_pendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_writeCipher = this.m_pendingCipher;
+ m_writeSeqNo.Reset();
+ }
+
+ /// <exception cref="IOException"/>
+ internal void FinaliseHandshake()
+ {
+ if (m_readCipher != m_pendingCipher || m_writeCipher != m_pendingCipher)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ this.m_pendingCipher = null;
+ }
+
+ internal bool NeedsKeyUpdate()
+ {
+ return m_writeSeqNo.CurrentValue >= (1L << 20);
+ }
+
+ /// <exception cref="IOException"/>
+ internal void NotifyKeyUpdateReceived()
+ {
+ m_readCipher.RekeyDecoder();
+ m_readSeqNo.Reset();
+ }
+
+ /// <exception cref="IOException"/>
+ internal void NotifyKeyUpdateSent()
+ {
+ m_writeCipher.RekeyEncoder();
+ m_writeSeqNo.Reset();
+ }
+
+ /// <exception cref="IOException"/>
+ internal RecordPreview PreviewRecordHeader(byte[] recordHeader)
+ {
+ short recordType = CheckRecordType(recordHeader, RecordFormat.TypeOffset);
+
+ //ProtocolVersion recordVersion = TlsUtilities.ReadVersion(recordHeader, RecordFormat.VersionOffset);
+
+ int length = TlsUtilities.ReadUint16(recordHeader, RecordFormat.LengthOffset);
+
+ CheckLength(length, m_ciphertextLimit, AlertDescription.record_overflow);
+
+ int recordSize = RecordFormat.FragmentOffset + length;
+ int applicationDataLimit = 0;
+
+ // NOTE: For TLS 1.3, this only MIGHT be application data
+ if (ContentType.application_data == recordType && m_handler.IsApplicationDataReady)
+ {
+ applicationDataLimit = System.Math.Max(0, System.Math.Min(m_plaintextLimit,
+ m_readCipher.GetPlaintextLimit(length)));
+ }
+
+ return new RecordPreview(recordSize, applicationDataLimit);
+ }
+
+ internal RecordPreview PreviewOutputRecord(int contentLength)
+ {
+ int contentLimit = System.Math.Max(0, System.Math.Min(m_plaintextLimit, contentLength));
+ int recordSize = PreviewOutputRecordSize(contentLimit);
+ return new RecordPreview(recordSize, contentLimit);
+ }
+
+ internal int PreviewOutputRecordSize(int contentLength)
+ {
+ Debug.Assert(contentLength <= m_plaintextLimit);
+
+ return RecordFormat.FragmentOffset + m_writeCipher.GetCiphertextEncodeLimit(contentLength, m_plaintextLimit);
+ }
+
+ /// <exception cref="IOException"/>
+ internal bool ReadFullRecord(byte[] input, int inputOff, int inputLen)
+ {
+ if (inputLen < RecordFormat.FragmentOffset)
+ return false;
+
+ int length = TlsUtilities.ReadUint16(input, inputOff + RecordFormat.LengthOffset);
+ if (inputLen != (RecordFormat.FragmentOffset + length))
+ return false;
+
+ short recordType = CheckRecordType(input, inputOff + RecordFormat.TypeOffset);
+
+ ProtocolVersion recordVersion = TlsUtilities.ReadVersion(input, inputOff + RecordFormat.VersionOffset);
+
+ CheckLength(length, m_ciphertextLimit, AlertDescription.record_overflow);
+
+ if (m_ignoreChangeCipherSpec && ContentType.change_cipher_spec == recordType)
+ {
+ CheckChangeCipherSpec(input, inputOff + RecordFormat.FragmentOffset, length);
+ return true;
+ }
+
+ TlsDecodeResult decoded = DecodeAndVerify(recordType, recordVersion, input,
+ inputOff + RecordFormat.FragmentOffset, length);
+
+ m_handler.ProcessRecord(decoded.contentType, decoded.buf, decoded.off, decoded.len);
+ return true;
+ }
+
+ /// <exception cref="IOException"/>
+ internal bool ReadRecord()
+ {
+ if (!m_inputRecord.ReadHeader(m_input))
+ return false;
+
+ short recordType = CheckRecordType(m_inputRecord.m_buf, RecordFormat.TypeOffset);
+
+ ProtocolVersion recordVersion = TlsUtilities.ReadVersion(m_inputRecord.m_buf, RecordFormat.VersionOffset);
+
+ int length = TlsUtilities.ReadUint16(m_inputRecord.m_buf, RecordFormat.LengthOffset);
+
+ CheckLength(length, m_ciphertextLimit, AlertDescription.record_overflow);
+
+ m_inputRecord.ReadFragment(m_input, length);
+
+ TlsDecodeResult decoded;
+ try
+ {
+ if (m_ignoreChangeCipherSpec && ContentType.change_cipher_spec == recordType)
+ {
+ CheckChangeCipherSpec(m_inputRecord.m_buf, RecordFormat.FragmentOffset, length);
+ return true;
+ }
+
+ decoded = DecodeAndVerify(recordType, recordVersion, m_inputRecord.m_buf, RecordFormat.FragmentOffset,
+ length);
+ }
+ finally
+ {
+ m_inputRecord.Reset();
+ }
+
+ m_handler.ProcessRecord(decoded.contentType, decoded.buf, decoded.off, decoded.len);
+ return true;
+ }
+
+ /// <exception cref="IOException"/>
+ internal TlsDecodeResult DecodeAndVerify(short recordType, ProtocolVersion recordVersion, byte[] ciphertext,
+ int off, int len)
+ {
+ long seqNo = m_readSeqNo.NextValue(AlertDescription.unexpected_message);
+ TlsDecodeResult decoded = m_readCipher.DecodeCiphertext(seqNo, recordType, recordVersion, ciphertext, off,
+ len);
+
+ CheckLength(decoded.len, m_plaintextLimit, AlertDescription.record_overflow);
+
+ /*
+ * RFC 5246 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (decoded.len < 1 && decoded.contentType != ContentType.application_data)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return decoded;
+ }
+
+ /// <exception cref="IOException"/>
+ internal void WriteRecord(short contentType, byte[] plaintext, int plaintextOffset, int plaintextLength)
+ {
+ // Never send anything until a valid ClientHello has been received
+ if (m_writeVersion == null)
+ return;
+
+ /*
+ * RFC 5246 6.2.1 The length should not exceed 2^14.
+ */
+ CheckLength(plaintextLength, m_plaintextLimit, AlertDescription.internal_error);
+
+ /*
+ * RFC 5246 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (plaintextLength < 1 && contentType != ContentType.application_data)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ long seqNo = m_writeSeqNo.NextValue(AlertDescription.internal_error);
+ ProtocolVersion recordVersion = m_writeVersion;
+
+ TlsEncodeResult encoded = m_writeCipher.EncodePlaintext(seqNo, contentType, recordVersion,
+ RecordFormat.FragmentOffset, plaintext, plaintextOffset, plaintextLength);
+
+ int ciphertextLength = encoded.len - RecordFormat.FragmentOffset;
+ TlsUtilities.CheckUint16(ciphertextLength);
+
+ TlsUtilities.WriteUint8(encoded.recordType, encoded.buf, encoded.off + RecordFormat.TypeOffset);
+ TlsUtilities.WriteVersion(recordVersion, encoded.buf, encoded.off + RecordFormat.VersionOffset);
+ TlsUtilities.WriteUint16(ciphertextLength, encoded.buf, encoded.off + RecordFormat.LengthOffset);
+
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //try
+ //{
+ m_output.Write(encoded.buf, encoded.off, encoded.len);
+ //}
+ //catch (InterruptedIOException e)
+ //{
+ // throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ //}
+
+ m_output.Flush();
+ }
+
+ /// <exception cref="IOException"/>
+ internal void Close()
+ {
+ m_inputRecord.Reset();
+
+ IOException io = null;
+ try
+ {
+ Platform.Dispose(m_input);
+ }
+ catch (IOException e)
+ {
+ io = e;
+ }
+
+ try
+ {
+ Platform.Dispose(m_output);
+ }
+ catch (IOException e)
+ {
+ if (io == null)
+ {
+ io = e;
+ }
+ else
+ {
+ // TODO[tls] Available from JDK 7
+ //io.addSuppressed(e);
+ }
+ }
+
+ if (io != null)
+ throw io;
+ }
+
+ /// <exception cref="IOException"/>
+ private void CheckChangeCipherSpec(byte[] buf, int off, int len)
+ {
+ if (1 != len || (byte)ChangeCipherSpec.change_cipher_spec != buf[off])
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message,
+ "Malformed " + ContentType.GetText(ContentType.change_cipher_spec));
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private short CheckRecordType(byte[] buf, int off)
+ {
+ short recordType = TlsUtilities.ReadUint8(buf, off);
+
+ if (null != m_readCipherDeferred && recordType == ContentType.application_data)
+ {
+ this.m_readCipher = m_readCipherDeferred;
+ this.m_readCipherDeferred = null;
+ this.m_ciphertextLimit = m_readCipher.GetCiphertextDecodeLimit(m_plaintextLimit);
+ m_readSeqNo.Reset();
+ }
+ else if (m_readCipher.UsesOpaqueRecordType)
+ {
+ if (ContentType.application_data != recordType)
+ {
+ if (m_ignoreChangeCipherSpec && ContentType.change_cipher_spec == recordType)
+ {
+ // See RFC 8446 D.4.
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message,
+ "Opaque " + ContentType.GetText(recordType));
+ }
+ }
+ }
+ else
+ {
+ switch (recordType)
+ {
+ case ContentType.application_data:
+ {
+ if (!m_handler.IsApplicationDataReady)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message,
+ "Not ready for " + ContentType.GetText(ContentType.application_data));
+ }
+ break;
+ }
+ case ContentType.alert:
+ case ContentType.change_cipher_spec:
+ case ContentType.handshake:
+ // case ContentType.heartbeat:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message,
+ "Unsupported " + ContentType.GetText(recordType));
+ }
+ }
+
+ return recordType;
+ }
+
+ /// <exception cref="IOException"/>
+ private static void CheckLength(int length, int limit, short alertDescription)
+ {
+ if (length > limit)
+ throw new TlsFatalAlert(alertDescription);
+ }
+
+ private sealed class Record
+ {
+ private readonly byte[] m_header = new byte[RecordFormat.FragmentOffset];
+
+ internal volatile byte[] m_buf;
+ internal volatile int m_pos;
+
+ internal Record()
+ {
+ this.m_buf = m_header;
+ this.m_pos = 0;
+ }
+
+ /// <exception cref="IOException"/>
+ internal void FillTo(Stream input, int length)
+ {
+ while (m_pos < length)
+ {
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //try
+ //{
+ int numRead = input.Read(m_buf, m_pos, length - m_pos);
+ if (numRead < 1)
+ break;
+
+ m_pos += numRead;
+ //}
+ //catch (InterruptedIOException e)
+ //{
+ // /*
+ // * Although modifying the bytesTransferred doesn't seem ideal, it's the simplest
+ // * way to make sure we don't break client code that depends on the exact type,
+ // * e.g. in Apache's httpcomponents-core-4.4.9, BHttpConnectionBase.isStale
+ // * depends on the exception type being SocketTimeoutException (or a subclass).
+ // *
+ // * We can set to 0 here because the only relevant callstack (via
+ // * TlsProtocol.readApplicationData) only ever processes one non-empty record (so
+ // * interruption after partial output cannot occur).
+ // */
+ // m_pos += e.bytesTransferred;
+ // e.bytesTransferred = 0;
+ // throw e;
+ //}
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal void ReadFragment(Stream input, int fragmentLength)
+ {
+ int recordLength = RecordFormat.FragmentOffset + fragmentLength;
+ Resize(recordLength);
+ FillTo(input, recordLength);
+ if (m_pos < recordLength)
+ throw new EndOfStreamException();
+ }
+
+ /// <exception cref="IOException"/>
+ internal bool ReadHeader(Stream input)
+ {
+ FillTo(input, RecordFormat.FragmentOffset);
+ if (m_pos == 0)
+ return false;
+
+ if (m_pos < RecordFormat.FragmentOffset)
+ throw new EndOfStreamException();
+
+ return true;
+ }
+
+ internal void Reset()
+ {
+ m_buf = m_header;
+ m_pos = 0;
+ }
+
+ private void Resize(int length)
+ {
+ if (m_buf.Length < length)
+ {
+ byte[] tmp = new byte[length];
+ Array.Copy(m_buf, 0, tmp, 0, m_pos);
+ m_buf = tmp;
+ }
+ }
+ }
+
+ private sealed class SequenceNumber
+ {
+ private long m_value = 0L;
+ private bool m_exhausted = false;
+
+ internal long CurrentValue
+ {
+ get { lock (this) return m_value; }
+ }
+
+ /// <exception cref="TlsFatalAlert"/>
+ internal long NextValue(short alertDescription)
+ {
+ lock (this)
+ {
+ if (m_exhausted)
+ throw new TlsFatalAlert(alertDescription, "Sequence numbers exhausted");
+
+ long result = m_value;
+ if (++m_value == 0L)
+ {
+ this.m_exhausted = true;
+ }
+ return result;
+ }
+ }
+
+ internal void Reset()
+ {
+ lock (this)
+ {
+ this.m_value = 0L;
+ this.m_exhausted = false;
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/SecurityParameters.cs b/crypto/src/tls/SecurityParameters.cs
new file mode 100644
index 000000000..84c3b9f11
--- /dev/null
+++ b/crypto/src/tls/SecurityParameters.cs
@@ -0,0 +1,334 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class SecurityParameters
+ {
+ internal int m_entity = -1;
+ internal bool m_secureRenegotiation = false;
+ internal int m_cipherSuite = Tls.CipherSuite.TLS_NULL_WITH_NULL_NULL;
+ internal short m_maxFragmentLength = -1;
+ internal int m_prfAlgorithm = -1;
+ internal short m_prfHashAlgorithm = -1;
+ internal int m_prfHashLength = -1;
+ internal int m_verifyDataLength = -1;
+ internal TlsSecret m_baseKeyClient = null;
+ internal TlsSecret m_baseKeyServer = null;
+ internal TlsSecret m_earlyExporterMasterSecret = null;
+ internal TlsSecret m_earlySecret = null;
+ internal TlsSecret m_exporterMasterSecret = null;
+ internal TlsSecret m_handshakeSecret = null;
+ internal TlsSecret m_masterSecret = null;
+ internal TlsSecret m_sharedSecret = null;
+ internal TlsSecret m_trafficSecretClient = null;
+ internal TlsSecret m_trafficSecretServer = null;
+ internal byte[] m_clientRandom = null;
+ internal byte[] m_serverRandom = null;
+ internal byte[] m_sessionHash = null;
+ internal byte[] m_sessionID = null;
+ internal byte[] m_psk = null;
+ internal byte[] m_pskIdentity = null;
+ internal byte[] m_srpIdentity = null;
+ internal byte[] m_tlsServerEndPoint = null;
+ internal byte[] m_tlsUnique = null;
+ internal bool m_encryptThenMac = false;
+ internal bool m_extendedMasterSecret = false;
+ internal bool m_extendedPadding = false;
+ internal bool m_truncatedHmac = false;
+ internal ProtocolName m_applicationProtocol = null;
+ internal bool m_applicationProtocolSet = false;
+ internal short[] m_clientCertTypes = null;
+ internal IList m_clientServerNames = null;
+ internal IList m_clientSigAlgs = null;
+ internal IList m_clientSigAlgsCert = null;
+ internal int[] m_clientSupportedGroups = null;
+ internal IList m_serverSigAlgs = null;
+ internal IList m_serverSigAlgsCert = null;
+ internal int[] m_serverSupportedGroups = null;
+ internal int m_keyExchangeAlgorithm = -1;
+ internal Certificate m_localCertificate = null;
+ internal Certificate m_peerCertificate = null;
+ internal ProtocolVersion m_negotiatedVersion = null;
+ internal int m_statusRequestVersion = 0;
+
+ // TODO[tls-ops] Investigate whether we can handle verify data using TlsSecret
+ internal byte[] m_localVerifyData = null;
+ internal byte[] m_peerVerifyData = null;
+
+ internal void Clear()
+ {
+ this.m_sessionHash = null;
+ this.m_sessionID = null;
+ this.m_clientCertTypes = null;
+ this.m_clientServerNames = null;
+ this.m_clientSigAlgs = null;
+ this.m_clientSigAlgsCert = null;
+ this.m_clientSupportedGroups = null;
+ this.m_serverSigAlgs = null;
+ this.m_serverSigAlgsCert = null;
+ this.m_serverSupportedGroups = null;
+ this.m_statusRequestVersion = 0;
+
+ this.m_baseKeyClient = ClearSecret(m_baseKeyClient);
+ this.m_baseKeyServer = ClearSecret(m_baseKeyServer);
+ this.m_earlyExporterMasterSecret = ClearSecret(m_earlyExporterMasterSecret);
+ this.m_earlySecret = ClearSecret(m_earlySecret);
+ this.m_exporterMasterSecret = ClearSecret(m_exporterMasterSecret);
+ this.m_handshakeSecret = ClearSecret(m_handshakeSecret);
+ this.m_masterSecret = ClearSecret(m_masterSecret);
+ this.m_sharedSecret = ClearSecret(m_sharedSecret);
+ }
+
+ public ProtocolName ApplicationProtocol
+ {
+ get { return m_applicationProtocol; }
+ }
+
+ public TlsSecret BaseKeyClient
+ {
+ get { return m_baseKeyClient; }
+ }
+
+ public TlsSecret BaseKeyServer
+ {
+ get { return m_baseKeyServer; }
+ }
+
+ public int CipherSuite
+ {
+ get { return m_cipherSuite; }
+ }
+
+ public short[] ClientCertTypes
+ {
+ get { return m_clientCertTypes; }
+ }
+
+ public byte[] ClientRandom
+ {
+ get { return m_clientRandom; }
+ }
+
+ public IList ClientServerNames
+ {
+ get { return m_clientServerNames; }
+ }
+
+ public IList ClientSigAlgs
+ {
+ get { return m_clientSigAlgs; }
+ }
+
+ public IList ClientSigAlgsCert
+ {
+ get { return m_clientSigAlgsCert; }
+ }
+
+ public int[] ClientSupportedGroups
+ {
+ get { return m_clientSupportedGroups; }
+ }
+
+ public TlsSecret EarlyExporterMasterSecret
+ {
+ get { return m_earlyExporterMasterSecret; }
+ }
+
+ public TlsSecret EarlySecret
+ {
+ get { return m_earlySecret; }
+ }
+
+ public TlsSecret ExporterMasterSecret
+ {
+ get { return m_exporterMasterSecret; }
+ }
+
+ public int Entity
+ {
+ get { return m_entity; }
+ }
+
+ public TlsSecret HandshakeSecret
+ {
+ get { return m_handshakeSecret; }
+ }
+
+ public bool IsApplicationProtocolSet
+ {
+ get { return m_applicationProtocolSet; }
+ }
+
+ public bool IsEncryptThenMac
+ {
+ get { return m_encryptThenMac; }
+ }
+
+ public bool IsExtendedMasterSecret
+ {
+ get { return m_extendedMasterSecret; }
+ }
+
+ public bool IsExtendedPadding
+ {
+ get { return m_extendedPadding; }
+ }
+
+ public bool IsSecureRenegotiation
+ {
+ get { return m_secureRenegotiation; }
+ }
+
+ public bool IsTruncatedHmac
+ {
+ get { return m_truncatedHmac; }
+ }
+
+ public int KeyExchangeAlgorithm
+ {
+ get { return m_keyExchangeAlgorithm; }
+ }
+
+ public Certificate LocalCertificate
+ {
+ get { return m_localCertificate; }
+ }
+
+ public byte[] LocalVerifyData
+ {
+ get { return m_localVerifyData; }
+ }
+
+ public TlsSecret MasterSecret
+ {
+ get { return m_masterSecret; }
+ }
+
+ public short MaxFragmentLength
+ {
+ get { return m_maxFragmentLength; }
+ }
+
+ public ProtocolVersion NegotiatedVersion
+ {
+ get { return m_negotiatedVersion; }
+ }
+
+ public Certificate PeerCertificate
+ {
+ get { return m_peerCertificate; }
+ }
+
+ public byte[] PeerVerifyData
+ {
+ get { return m_peerVerifyData; }
+ }
+
+ public int PrfAlgorithm
+ {
+ get { return m_prfAlgorithm; }
+ }
+
+ public short PrfHashAlgorithm
+ {
+ get { return m_prfHashAlgorithm; }
+ }
+
+ public int PrfHashLength
+ {
+ get { return m_prfHashLength; }
+ }
+
+ public byte[] Psk
+ {
+ get { return m_psk; }
+ }
+
+ public byte[] PskIdentity
+ {
+ get { return m_pskIdentity; }
+ }
+
+ public byte[] ServerRandom
+ {
+ get { return m_serverRandom; }
+ }
+
+ public IList ServerSigAlgs
+ {
+ get { return m_serverSigAlgs; }
+ }
+
+ public IList ServerSigAlgsCert
+ {
+ get { return m_serverSigAlgsCert; }
+ }
+
+ public int[] ServerSupportedGroups
+ {
+ get { return m_serverSupportedGroups; }
+ }
+
+ public byte[] SessionHash
+ {
+ get { return m_sessionHash; }
+ }
+
+ public byte[] SessionID
+ {
+ get { return m_sessionID; }
+ }
+
+ public TlsSecret SharedSecret
+ {
+ get { return m_sharedSecret; }
+ }
+
+ public byte[] SrpIdentity
+ {
+ get { return m_srpIdentity; }
+ }
+
+ public int StatusRequestVersion
+ {
+ get { return m_statusRequestVersion; }
+ }
+
+ public byte[] TlsServerEndPoint
+ {
+ get { return m_tlsServerEndPoint; }
+ }
+
+ public byte[] TlsUnique
+ {
+ get { return m_tlsUnique; }
+ }
+
+ public TlsSecret TrafficSecretClient
+ {
+ get { return m_trafficSecretClient; }
+ }
+
+ public TlsSecret TrafficSecretServer
+ {
+ get { return m_trafficSecretServer; }
+ }
+
+ public int VerifyDataLength
+ {
+ get { return m_verifyDataLength; }
+ }
+
+ private static TlsSecret ClearSecret(TlsSecret secret)
+ {
+ if (null != secret)
+ {
+ secret.Destroy();
+ }
+ return null;
+ }
+ }
+}
diff --git a/crypto/src/tls/ServerHello.cs b/crypto/src/tls/ServerHello.cs
new file mode 100644
index 000000000..15cc09032
--- /dev/null
+++ b/crypto/src/tls/ServerHello.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class ServerHello
+ {
+ private static readonly byte[] HelloRetryRequestMagic = { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE,
+ 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09,
+ 0xE2, 0xC8, 0xA8, 0x33, 0x9C };
+
+ private readonly ProtocolVersion m_version;
+ private readonly byte[] m_random;
+ private readonly byte[] m_sessionID;
+ private readonly int m_cipherSuite;
+ private readonly IDictionary m_extensions;
+
+ public ServerHello(byte[] sessionID, int cipherSuite, IDictionary extensions)
+ : this(ProtocolVersion.TLSv12, Arrays.Clone(HelloRetryRequestMagic), sessionID, cipherSuite, extensions)
+ {
+ }
+
+ public ServerHello(ProtocolVersion version, byte[] random, byte[] sessionID, int cipherSuite,
+ IDictionary extensions)
+ {
+ this.m_version = version;
+ this.m_random = random;
+ this.m_sessionID = sessionID;
+ this.m_cipherSuite = cipherSuite;
+ this.m_extensions = extensions;
+ }
+
+ public int CipherSuite
+ {
+ get { return m_cipherSuite; }
+ }
+
+ public IDictionary Extensions
+ {
+ get { return m_extensions; }
+ }
+
+ public byte[] Random
+ {
+ get { return m_random; }
+ }
+
+ public byte[] SessionID
+ {
+ get { return m_sessionID; }
+ }
+
+ public ProtocolVersion Version
+ {
+ get { return m_version; }
+ }
+
+ public bool IsHelloRetryRequest()
+ {
+ return Arrays.AreEqual(HelloRetryRequestMagic, m_random);
+ }
+
+ /// <summary>Encode this <see cref="ServerHello"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(TlsContext context, Stream output)
+ {
+ TlsUtilities.WriteVersion(m_version, output);
+
+ output.Write(m_random, 0, m_random.Length);
+
+ TlsUtilities.WriteOpaque8(m_sessionID, output);
+
+ TlsUtilities.WriteUint16(m_cipherSuite, output);
+
+ TlsUtilities.WriteUint8(CompressionMethod.cls_null, output);
+
+ TlsProtocol.WriteExtensions(output, m_extensions);
+ }
+
+ /// <summary>Parse a <see cref="ServerHello"/> from a <see cref="MemoryStream"/>.</summary>
+ /// <param name="input">the <see cref="MemoryStream"/> to parse from.</param>
+ /// <returns>a <see cref="ServerHello"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static ServerHello Parse(MemoryStream input)
+ {
+ ProtocolVersion version = TlsUtilities.ReadVersion(input);
+
+ byte[] random = TlsUtilities.ReadFully(32, input);
+
+ byte[] sessionID = TlsUtilities.ReadOpaque8(input, 0, 32);
+
+ int cipherSuite = TlsUtilities.ReadUint16(input);
+
+ short compressionMethod = TlsUtilities.ReadUint8(input);
+ if (CompressionMethod.cls_null != compressionMethod)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ IDictionary extensions = TlsProtocol.ReadExtensions(input);
+
+ return new ServerHello(version, random, sessionID, cipherSuite, extensions);
+ }
+ }
+}
diff --git a/crypto/src/tls/ServerName.cs b/crypto/src/tls/ServerName.cs
new file mode 100644
index 000000000..da40c2c8a
--- /dev/null
+++ b/crypto/src/tls/ServerName.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 6066 3. Server Name Indication</summary>
+ /// <remarks>
+ /// Current implementation uses this guidance: "For backward compatibility, all future data structures associated
+ /// with new NameTypes MUST begin with a 16-bit length field. TLS MAY treat provided server names as opaque data
+ /// and pass the names and types to the application.". RFC 6066 specifies ASCII encoding for host_name (possibly
+ /// using A-labels for IDNs), but note that the previous version (RFC 4366) specified UTF-8 encoding (see RFC 6066
+ /// Appendix A). For maximum compatibility, it is recommended that client code tolerate receiving UTF-8 from the
+ /// peer, but only generate ASCII itself.
+ /// </remarks>
+ public sealed class ServerName
+ {
+ private readonly short nameType;
+ private readonly byte[] nameData;
+
+ public ServerName(short nameType, byte[] nameData)
+ {
+ if (!TlsUtilities.IsValidUint8(nameType))
+ throw new ArgumentException("must be from 0 to 255", "nameType");
+ if (null == nameData)
+ throw new ArgumentNullException("nameData");
+ if (nameData.Length < 1 || !TlsUtilities.IsValidUint16(nameData.Length))
+ throw new ArgumentException("must have length from 1 to 65535", "nameData");
+
+ this.nameType = nameType;
+ this.nameData = nameData;
+ }
+
+ public byte[] NameData
+ {
+ get { return nameData; }
+ }
+
+ public short NameType
+ {
+ get { return nameType; }
+ }
+
+ /// <summary>Encode this <see cref="ServerName"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(nameType, output);
+ TlsUtilities.WriteOpaque16(nameData, output);
+ }
+
+ /// <summary>Parse a <see cref="ServerName"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="ServerName"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static ServerName Parse(Stream input)
+ {
+ short name_type = TlsUtilities.ReadUint8(input);
+ byte[] nameData = TlsUtilities.ReadOpaque16(input, 1);
+ return new ServerName(name_type, nameData);
+ }
+ }
+}
diff --git a/crypto/src/tls/ServerNameList.cs b/crypto/src/tls/ServerNameList.cs
new file mode 100644
index 000000000..915e94390
--- /dev/null
+++ b/crypto/src/tls/ServerNameList.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class ServerNameList
+ {
+ private readonly IList m_serverNameList;
+
+ /// <param name="serverNameList">an <see cref="IList"/> of <see cref="ServerName"/>.</param>
+ public ServerNameList(IList serverNameList)
+ {
+ if (null == serverNameList)
+ throw new ArgumentNullException("serverNameList");
+
+ this.m_serverNameList = serverNameList;
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="ServerName"/>.</returns>
+ public IList ServerNames
+ {
+ get { return m_serverNameList; }
+ }
+
+ /// <summary>Encode this <see cref="ServerNameList"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to .</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ short[] nameTypesSeen = TlsUtilities.EmptyShorts;
+ foreach (ServerName entry in ServerNames)
+ {
+ nameTypesSeen = CheckNameType(nameTypesSeen, entry.NameType);
+ if (null == nameTypesSeen)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ entry.Encode(buf);
+ }
+
+ int length = (int)buf.Length;
+ TlsUtilities.CheckUint16(length);
+ TlsUtilities.WriteUint16(length, output);
+ Streams.WriteBufTo(buf, output);
+ }
+
+ /// <summary>Parse a <see cref="ServerNameList"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="ServerNameList"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static ServerNameList Parse(Stream input)
+ {
+ byte[] data = TlsUtilities.ReadOpaque16(input, 1);
+
+ MemoryStream buf = new MemoryStream(data, false);
+
+ short[] nameTypesSeen = TlsUtilities.EmptyShorts;
+ IList server_name_list = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ ServerName entry = ServerName.Parse(buf);
+
+ nameTypesSeen = CheckNameType(nameTypesSeen, entry.NameType);
+ if (null == nameTypesSeen)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ server_name_list.Add(entry);
+ }
+
+ return new ServerNameList(server_name_list);
+ }
+
+ private static short[] CheckNameType(short[] nameTypesSeen, short nameType)
+ {
+ // RFC 6066 3. The ServerNameList MUST NOT contain more than one name of the same NameType.
+ if (Arrays.Contains(nameTypesSeen, nameType))
+ return null;
+
+ return Arrays.Append(nameTypesSeen, nameType);
+ }
+ }
+}
diff --git a/crypto/src/tls/ServerOnlyTlsAuthentication.cs b/crypto/src/tls/ServerOnlyTlsAuthentication.cs
new file mode 100644
index 000000000..5a8d59904
--- /dev/null
+++ b/crypto/src/tls/ServerOnlyTlsAuthentication.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class ServerOnlyTlsAuthentication
+ : TlsAuthentication
+ {
+ public abstract void NotifyServerCertificate(TlsServerCertificate serverCertificate);
+
+ public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ return null;
+ }
+ }
+}
diff --git a/crypto/src/tls/ServerSrpParams.cs b/crypto/src/tls/ServerSrpParams.cs
new file mode 100644
index 000000000..9aca882f6
--- /dev/null
+++ b/crypto/src/tls/ServerSrpParams.cs
@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class ServerSrpParams
+ {
+ private BigInteger m_N, m_g, m_B;
+ private 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 BigInteger B
+ {
+ get { return m_B; }
+ }
+
+ public BigInteger G
+ {
+ get { return m_g; }
+ }
+
+ public BigInteger N
+ {
+ get { return m_N; }
+ }
+
+ public byte[] S
+ {
+ get { return m_s; }
+ }
+
+ /// <summary>Encode this <see cref="ServerSrpParams"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsSrpUtilities.WriteSrpParameter(m_N, output);
+ TlsSrpUtilities.WriteSrpParameter(m_g, output);
+ TlsUtilities.WriteOpaque8(m_s, output);
+ TlsSrpUtilities.WriteSrpParameter(m_B, output);
+ }
+
+ /// <summary>Parse a <see cref="ServerSrpParams"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="ServerSrpParams"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static ServerSrpParams Parse(Stream input)
+ {
+ BigInteger N = TlsSrpUtilities.ReadSrpParameter(input);
+ BigInteger g = TlsSrpUtilities.ReadSrpParameter(input);
+ byte[] s = TlsUtilities.ReadOpaque8(input, 1);
+ BigInteger B = TlsSrpUtilities.ReadSrpParameter(input);
+
+ return new ServerSrpParams(N, g, s, B);
+ }
+ }
+}
diff --git a/crypto/src/tls/SessionParameters.cs b/crypto/src/tls/SessionParameters.cs
new file mode 100644
index 000000000..9a62e351c
--- /dev/null
+++ b/crypto/src/tls/SessionParameters.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class SessionParameters
+ {
+ public sealed class Builder
+ {
+ private int m_cipherSuite = -1;
+ private Certificate m_localCertificate = null;
+ private TlsSecret m_masterSecret = null;
+ private ProtocolVersion m_negotiatedVersion;
+ private Certificate m_peerCertificate = null;
+ private byte[] m_pskIdentity = null;
+ private byte[] m_srpIdentity = null;
+ private byte[] m_encodedServerExtensions = null;
+ private bool m_extendedMasterSecret = false;
+
+ public Builder()
+ {
+ }
+
+ public SessionParameters Build()
+ {
+ Validate(m_cipherSuite >= 0, "cipherSuite");
+ Validate(m_masterSecret != null, "masterSecret");
+ return new SessionParameters(m_cipherSuite, m_localCertificate, m_masterSecret, m_negotiatedVersion,
+ m_peerCertificate, m_pskIdentity, m_srpIdentity, m_encodedServerExtensions, m_extendedMasterSecret);
+ }
+
+ public Builder SetCipherSuite(int cipherSuite)
+ {
+ this.m_cipherSuite = cipherSuite;
+ return this;
+ }
+
+ public Builder SetExtendedMasterSecret(bool extendedMasterSecret)
+ {
+ this.m_extendedMasterSecret = extendedMasterSecret;
+ return this;
+ }
+
+ public Builder SetLocalCertificate(Certificate localCertificate)
+ {
+ this.m_localCertificate = localCertificate;
+ return this;
+ }
+
+ public Builder SetMasterSecret(TlsSecret masterSecret)
+ {
+ this.m_masterSecret = masterSecret;
+ return this;
+ }
+
+ public Builder SetNegotiatedVersion(ProtocolVersion negotiatedVersion)
+ {
+ this.m_negotiatedVersion = negotiatedVersion;
+ return this;
+ }
+
+ public Builder SetPeerCertificate(Certificate peerCertificate)
+ {
+ this.m_peerCertificate = peerCertificate;
+ return this;
+ }
+
+ public Builder SetPskIdentity(byte[] pskIdentity)
+ {
+ this.m_pskIdentity = pskIdentity;
+ return this;
+ }
+
+ public Builder SetSrpIdentity(byte[] srpIdentity)
+ {
+ this.m_srpIdentity = srpIdentity;
+ return this;
+ }
+
+ /// <exception cref="IOException"/>
+ public Builder SetServerExtensions(IDictionary serverExtensions)
+ {
+ if (serverExtensions == null || serverExtensions.Count < 1)
+ {
+ this.m_encodedServerExtensions = null;
+ }
+ else
+ {
+ MemoryStream buf = new MemoryStream();
+ TlsProtocol.WriteExtensions(buf, serverExtensions);
+ this.m_encodedServerExtensions = buf.ToArray();
+ }
+ return this;
+ }
+
+ private void Validate(bool condition, string parameter)
+ {
+ if (!condition)
+ throw new InvalidOperationException("Required session parameter '" + parameter + "' not configured");
+ }
+ }
+
+ private readonly int m_cipherSuite;
+ private readonly Certificate m_localCertificate;
+ private readonly TlsSecret m_masterSecret;
+ private readonly ProtocolVersion m_negotiatedVersion;
+ private readonly Certificate m_peerCertificate;
+ private readonly byte[] m_pskIdentity;
+ private readonly byte[] m_srpIdentity;
+ private readonly byte[] m_encodedServerExtensions;
+ private readonly bool m_extendedMasterSecret;
+
+ private SessionParameters(int cipherSuite, Certificate localCertificate, TlsSecret masterSecret,
+ ProtocolVersion negotiatedVersion, Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity,
+ byte[] encodedServerExtensions, bool extendedMasterSecret)
+ {
+ this.m_cipherSuite = cipherSuite;
+ this.m_localCertificate = localCertificate;
+ this.m_masterSecret = masterSecret;
+ this.m_negotiatedVersion = negotiatedVersion;
+ this.m_peerCertificate = peerCertificate;
+ this.m_pskIdentity = Arrays.Clone(pskIdentity);
+ this.m_srpIdentity = Arrays.Clone(srpIdentity);
+ this.m_encodedServerExtensions = encodedServerExtensions;
+ this.m_extendedMasterSecret = extendedMasterSecret;
+ }
+
+ public int CipherSuite
+ {
+ get { return m_cipherSuite; }
+ }
+
+ public void Clear()
+ {
+ if (m_masterSecret != null)
+ {
+ m_masterSecret.Destroy();
+ }
+ }
+
+ public SessionParameters Copy()
+ {
+ return new SessionParameters(m_cipherSuite, m_localCertificate, m_masterSecret, m_negotiatedVersion,
+ m_peerCertificate, m_pskIdentity, m_srpIdentity, m_encodedServerExtensions, m_extendedMasterSecret);
+ }
+
+ public bool IsExtendedMasterSecret
+ {
+ get { return m_extendedMasterSecret; }
+ }
+
+ public Certificate LocalCertificate
+ {
+ get { return m_localCertificate; }
+ }
+
+ public TlsSecret MasterSecret
+ {
+ get { return m_masterSecret; }
+ }
+
+ public ProtocolVersion NegotiatedVersion
+ {
+ get { return m_negotiatedVersion; }
+ }
+
+ public Certificate PeerCertificate
+ {
+ get { return m_peerCertificate; }
+ }
+
+ public byte[] PskIdentity
+ {
+ get { return m_pskIdentity; }
+ }
+
+ /// <exception cref="IOException"/>
+ public IDictionary ReadServerExtensions()
+ {
+ if (m_encodedServerExtensions == null)
+ return null;
+
+ return TlsProtocol.ReadExtensions(new MemoryStream(m_encodedServerExtensions, false));
+ }
+
+ public byte[] SrpIdentity
+ {
+ get { return m_srpIdentity; }
+ }
+ }
+}
diff --git a/crypto/src/tls/SignatureAlgorithm.cs b/crypto/src/tls/SignatureAlgorithm.cs
new file mode 100644
index 000000000..726504c52
--- /dev/null
+++ b/crypto/src/tls/SignatureAlgorithm.cs
@@ -0,0 +1,125 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /**
+ * RFC 5246 7.4.1.4.1 (in RFC 2246, there were no specific values assigned)
+ */
+ public class SignatureAlgorithm
+ {
+ public const short anonymous = 0;
+ public const short rsa = 1;
+ public const short dsa = 2;
+ public const short ecdsa = 3;
+
+ /*
+ * RFC 8422
+ */
+ public const short ed25519 = 7;
+ public const short ed448 = 8;
+
+ /*
+ * RFC 8446 (implied from SignatureScheme values)
+ * RFC 8447 reserved these values without allocating the implied names
+ */
+ public const short rsa_pss_rsae_sha256 = 4;
+ public const short rsa_pss_rsae_sha384 = 5;
+ public const short rsa_pss_rsae_sha512 = 6;
+ public const short rsa_pss_pss_sha256 = 9;
+ public const short rsa_pss_pss_sha384 = 10;
+ public const short rsa_pss_pss_sha512 = 11;
+
+ /*
+ * RFC 8734 (implied from SignatureScheme values)
+ */
+ public const short ecdsa_brainpoolP256r1tls13_sha256 = 26;
+ public const short ecdsa_brainpoolP384r1tls13_sha384 = 27;
+ public const short ecdsa_brainpoolP512r1tls13_sha512 = 28;
+
+ /*
+ * draft-smyshlyaev-tls12-gost-suites-10
+ */
+ public const short gostr34102012_256 = 64;
+ public const short gostr34102012_512 = 65;
+
+ public static short GetClientCertificateType(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return ClientCertificateType.rsa_sign;
+
+ case SignatureAlgorithm.dsa:
+ return ClientCertificateType.dss_sign;
+
+ case SignatureAlgorithm.ecdsa:
+ case SignatureAlgorithm.ed25519:
+ case SignatureAlgorithm.ed448:
+ return ClientCertificateType.ecdsa_sign;
+
+ case SignatureAlgorithm.gostr34102012_256:
+ return ClientCertificateType.gost_sign256;
+
+ case SignatureAlgorithm.gostr34102012_512:
+ return ClientCertificateType.gost_sign512;
+
+ default:
+ return -1;
+ }
+ }
+
+ public static string GetName(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case anonymous:
+ return "anonymous";
+ case rsa:
+ return "rsa";
+ case dsa:
+ return "dsa";
+ case ecdsa:
+ return "ecdsa";
+ case ed25519:
+ return "ed25519";
+ case ed448:
+ return "ed448";
+ case gostr34102012_256:
+ return "gostr34102012_256";
+ case gostr34102012_512:
+ return "gostr34102012_512";
+ case rsa_pss_rsae_sha256:
+ return "rsa_pss_rsae_sha256";
+ case rsa_pss_rsae_sha384:
+ return "rsa_pss_rsae_sha384";
+ case rsa_pss_rsae_sha512:
+ return "rsa_pss_rsae_sha512";
+ case rsa_pss_pss_sha256:
+ return "rsa_pss_pss_sha256";
+ case rsa_pss_pss_sha384:
+ return "rsa_pss_pss_sha384";
+ case rsa_pss_pss_sha512:
+ return "rsa_pss_pss_sha512";
+ case ecdsa_brainpoolP256r1tls13_sha256:
+ return "ecdsa_brainpoolP256r1tls13_sha256";
+ case ecdsa_brainpoolP384r1tls13_sha384:
+ return "ecdsa_brainpoolP384r1tls13_sha384";
+ case ecdsa_brainpoolP512r1tls13_sha512:
+ return "ecdsa_brainpoolP512r1tls13_sha512";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(short signatureAlgorithm)
+ {
+ return GetName(signatureAlgorithm) + "(" + signatureAlgorithm + ")";
+ }
+ }
+}
diff --git a/crypto/src/tls/SignatureAndHashAlgorithm.cs b/crypto/src/tls/SignatureAndHashAlgorithm.cs
new file mode 100644
index 000000000..9de2a42e4
--- /dev/null
+++ b/crypto/src/tls/SignatureAndHashAlgorithm.cs
@@ -0,0 +1,171 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5246 7.4.1.4.1</summary>
+ public sealed class SignatureAndHashAlgorithm
+ {
+ public static readonly SignatureAndHashAlgorithm ecdsa_brainpoolP256r1tls13_sha256 =
+ Create(SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256);
+ public static readonly SignatureAndHashAlgorithm ecdsa_brainpoolP384r1tls13_sha384 =
+ Create(SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384);
+ public static readonly SignatureAndHashAlgorithm ecdsa_brainpoolP512r1tls13_sha512 =
+ Create(SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512);
+ public static readonly SignatureAndHashAlgorithm ed25519 =
+ Create(SignatureScheme.ed25519);
+ public static readonly SignatureAndHashAlgorithm ed448 =
+ Create(SignatureScheme.ed448);
+ public static readonly SignatureAndHashAlgorithm gostr34102012_256 =
+ Create(HashAlgorithm.Intrinsic, SignatureAlgorithm.gostr34102012_256);
+ public static readonly SignatureAndHashAlgorithm gostr34102012_512 =
+ Create(HashAlgorithm.Intrinsic, SignatureAlgorithm.gostr34102012_512);
+ public static readonly SignatureAndHashAlgorithm rsa_pss_rsae_sha256 =
+ Create(SignatureScheme.rsa_pss_rsae_sha256);
+ public static readonly SignatureAndHashAlgorithm rsa_pss_rsae_sha384 =
+ Create(SignatureScheme.rsa_pss_rsae_sha384);
+ public static readonly SignatureAndHashAlgorithm rsa_pss_rsae_sha512 =
+ Create(SignatureScheme.rsa_pss_rsae_sha512);
+ public static readonly SignatureAndHashAlgorithm rsa_pss_pss_sha256 =
+ Create(SignatureScheme.rsa_pss_pss_sha256);
+ public static readonly SignatureAndHashAlgorithm rsa_pss_pss_sha384 =
+ Create(SignatureScheme.rsa_pss_pss_sha384);
+ public static readonly SignatureAndHashAlgorithm rsa_pss_pss_sha512 =
+ Create(SignatureScheme.rsa_pss_pss_sha512);
+
+ public static SignatureAndHashAlgorithm GetInstance(short hashAlgorithm, short signatureAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case HashAlgorithm.Intrinsic:
+ return GetInstanceIntrinsic(signatureAlgorithm);
+ default:
+ return Create(hashAlgorithm, signatureAlgorithm);
+ }
+ }
+
+ private static SignatureAndHashAlgorithm GetInstanceIntrinsic(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.ed25519:
+ return ed25519;
+ case SignatureAlgorithm.ed448:
+ return ed448;
+ case SignatureAlgorithm.gostr34102012_256:
+ return gostr34102012_256;
+ case SignatureAlgorithm.gostr34102012_512:
+ return gostr34102012_512;
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ return rsa_pss_rsae_sha256;
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ return rsa_pss_rsae_sha384;
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return rsa_pss_rsae_sha512;
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ return rsa_pss_pss_sha256;
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ return rsa_pss_pss_sha384;
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return rsa_pss_pss_sha512;
+ case SignatureAlgorithm.ecdsa_brainpoolP256r1tls13_sha256:
+ return ecdsa_brainpoolP256r1tls13_sha256;
+ case SignatureAlgorithm.ecdsa_brainpoolP384r1tls13_sha384:
+ return ecdsa_brainpoolP384r1tls13_sha384;
+ case SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512:
+ return ecdsa_brainpoolP512r1tls13_sha512;
+ default:
+ return Create(HashAlgorithm.Intrinsic, signatureAlgorithm);
+ }
+ }
+
+ private static SignatureAndHashAlgorithm Create(int signatureScheme)
+ {
+ short hashAlgorithm = SignatureScheme.GetHashAlgorithm(signatureScheme);
+ short signatureAlgorithm = SignatureScheme.GetSignatureAlgorithm(signatureScheme);
+ return Create(hashAlgorithm, signatureAlgorithm);
+ }
+
+ private static SignatureAndHashAlgorithm Create(short hashAlgorithm, short signatureAlgorithm)
+ {
+ return new SignatureAndHashAlgorithm(hashAlgorithm, signatureAlgorithm);
+ }
+
+ private readonly short m_hash;
+ private readonly short m_signature;
+
+ /// <param name="hash"><see cref="HashAlgorithm"/></param>
+ /// <param name="signature"><see cref="SignatureAlgorithm"/></param>
+ public SignatureAndHashAlgorithm(short hash, short signature)
+ {
+ /*
+ * TODO]tls] The TlsUtils methods are inlined here to avoid circular static initialization
+ * b/w these classes. We should refactor parts of TlsUtils into separate classes. e.g. the
+ * TLS low-level encoding methods, and/or the SigAndHash registry and methods.
+ */
+
+ //if (!TlsUtilities.IsValidUint8(hash))
+ if ((hash & 0xFF) != hash)
+ throw new ArgumentException("should be a uint8", "hash");
+
+ //if (!TlsUtilities.IsValidUint8(signature))
+ if ((signature & 0xFF) != signature)
+ throw new ArgumentException("should be a uint8", "signature");
+
+ this.m_hash = hash;
+ this.m_signature = signature;
+ }
+
+ /// <returns><see cref="HashAlgorithm"/></returns>
+ public short Hash
+ {
+ get { return m_hash; }
+ }
+
+ /// <returns><see cref="SignatureAlgorithm"/></returns>
+ public short Signature
+ {
+ get { return m_signature; }
+ }
+
+ /// <summary>Encode this <see cref="SignatureAndHashAlgorithm"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(Hash, output);
+ TlsUtilities.WriteUint8(Signature, output);
+ }
+
+ /// <summary>Parse a <see cref="SignatureAndHashAlgorithm"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="SignatureAndHashAlgorithm"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static SignatureAndHashAlgorithm Parse(Stream input)
+ {
+ short hash = TlsUtilities.ReadUint8(input);
+ short signature = TlsUtilities.ReadUint8(input);
+
+ return GetInstance(hash, signature);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is SignatureAndHashAlgorithm))
+ return false;
+
+ SignatureAndHashAlgorithm other = (SignatureAndHashAlgorithm)obj;
+ return other.Hash == Hash && other.Signature == Signature;
+ }
+
+ public override int GetHashCode()
+ {
+ return ((int)Hash << 16) | (int)Signature;
+ }
+
+ public override string ToString()
+ {
+ return "{" + HashAlgorithm.GetText(Hash) + "," + SignatureAlgorithm.GetText(Signature) + "}";
+ }
+ }
+}
diff --git a/crypto/src/tls/SignatureScheme.cs b/crypto/src/tls/SignatureScheme.cs
new file mode 100644
index 000000000..4b934133d
--- /dev/null
+++ b/crypto/src/tls/SignatureScheme.cs
@@ -0,0 +1,235 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class SignatureScheme
+ {
+ /*
+ * RFC 8446
+ */
+
+ public const int rsa_pkcs1_sha1 = 0x0201;
+ public const int ecdsa_sha1 = 0x0203;
+
+ public const int rsa_pkcs1_sha256 = 0x0401;
+ public const int rsa_pkcs1_sha384 = 0x0501;
+ public const int rsa_pkcs1_sha512 = 0x0601;
+
+ public const int ecdsa_secp256r1_sha256 = 0x0403;
+ public const int ecdsa_secp384r1_sha384 = 0x0503;
+ public const int ecdsa_secp521r1_sha512 = 0x0603;
+
+ public const int rsa_pss_rsae_sha256 = 0x0804;
+ public const int rsa_pss_rsae_sha384 = 0x0805;
+ public const int rsa_pss_rsae_sha512 = 0x0806;
+
+ public const int ed25519 = 0x0807;
+ public const int ed448 = 0x0808;
+
+ public const int rsa_pss_pss_sha256 = 0x0809;
+ public const int rsa_pss_pss_sha384 = 0x080A;
+ public const int rsa_pss_pss_sha512 = 0x080B;
+
+ /*
+ * RFC 8734
+ */
+
+ public const int ecdsa_brainpoolP256r1tls13_sha256 = 0x081A;
+ public const int ecdsa_brainpoolP384r1tls13_sha384 = 0x081B;
+ public const int ecdsa_brainpoolP512r1tls13_sha512 = 0x081C;
+
+ /*
+ * RFC 8998
+ */
+
+ public const int sm2sig_sm3 = 0x0708;
+
+ /*
+ * RFC 8446 reserved for private use (0xFE00..0xFFFF)
+ */
+
+ public static int From(SignatureAndHashAlgorithm sigAndHashAlg)
+ {
+ if (null == sigAndHashAlg)
+ throw new ArgumentNullException();
+
+ return From(sigAndHashAlg.Hash, sigAndHashAlg.Signature);
+ }
+
+ public static int From(short hashAlgorithm, short signatureAlgorithm)
+ {
+ return ((hashAlgorithm & 0xFF) << 8) | (signatureAlgorithm & 0xFF);
+ }
+
+ public static int GetCryptoHashAlgorithm(int signatureScheme)
+ {
+ switch (signatureScheme)
+ {
+ case ed25519:
+ case ed448:
+ return -1;
+ case ecdsa_brainpoolP256r1tls13_sha256:
+ case rsa_pss_pss_sha256:
+ case rsa_pss_rsae_sha256:
+ return CryptoHashAlgorithm.sha256;
+ case ecdsa_brainpoolP384r1tls13_sha384:
+ case rsa_pss_pss_sha384:
+ case rsa_pss_rsae_sha384:
+ return CryptoHashAlgorithm.sha384;
+ case ecdsa_brainpoolP512r1tls13_sha512:
+ case rsa_pss_pss_sha512:
+ case rsa_pss_rsae_sha512:
+ return CryptoHashAlgorithm.sha512;
+ case sm2sig_sm3:
+ return CryptoHashAlgorithm.sm3;
+ default:
+ {
+ short hashAlgorithm = GetHashAlgorithm(signatureScheme);
+ if (HashAlgorithm.Intrinsic == hashAlgorithm || !HashAlgorithm.IsRecognized(hashAlgorithm))
+ return -1;
+
+ return TlsCryptoUtilities.GetHash(GetHashAlgorithm(signatureScheme));
+ }
+ }
+ }
+
+ public static string GetName(int signatureScheme)
+ {
+ switch (signatureScheme)
+ {
+ case rsa_pkcs1_sha1:
+ return "rsa_pkcs1_sha1";
+ case ecdsa_sha1:
+ return "ecdsa_sha1";
+ case rsa_pkcs1_sha256:
+ return "rsa_pkcs1_sha256";
+ case rsa_pkcs1_sha384:
+ return "rsa_pkcs1_sha384";
+ case rsa_pkcs1_sha512:
+ return "rsa_pkcs1_sha512";
+ case ecdsa_secp256r1_sha256:
+ return "ecdsa_secp256r1_sha256";
+ case ecdsa_secp384r1_sha384:
+ return "ecdsa_secp384r1_sha384";
+ case ecdsa_secp521r1_sha512:
+ return "ecdsa_secp521r1_sha512";
+ case rsa_pss_rsae_sha256:
+ return "rsa_pss_rsae_sha256";
+ case rsa_pss_rsae_sha384:
+ return "rsa_pss_rsae_sha384";
+ case rsa_pss_rsae_sha512:
+ return "rsa_pss_rsae_sha512";
+ case ed25519:
+ return "ed25519";
+ case ed448:
+ return "ed448";
+ case rsa_pss_pss_sha256:
+ return "rsa_pss_pss_sha256";
+ case rsa_pss_pss_sha384:
+ return "rsa_pss_pss_sha384";
+ case rsa_pss_pss_sha512:
+ return "rsa_pss_pss_sha512";
+ case ecdsa_brainpoolP256r1tls13_sha256:
+ return "ecdsa_brainpoolP256r1tls13_sha256";
+ case ecdsa_brainpoolP384r1tls13_sha384:
+ return "ecdsa_brainpoolP384r1tls13_sha384";
+ case ecdsa_brainpoolP512r1tls13_sha512:
+ return "ecdsa_brainpoolP512r1tls13_sha512";
+ case sm2sig_sm3:
+ return "sm2sig_sm3";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * For TLS 1.3+ usage, some signature schemes are constrained to use a particular
+ * ({@link NamedGroup}. Not relevant for TLS 1.2 and below.
+ */
+ public static int GetNamedGroup(int signatureScheme)
+ {
+ switch (signatureScheme)
+ {
+ case ecdsa_brainpoolP256r1tls13_sha256:
+ return NamedGroup.brainpoolP256r1tls13;
+ case ecdsa_brainpoolP384r1tls13_sha384:
+ return NamedGroup.brainpoolP384r1tls13;
+ case ecdsa_brainpoolP512r1tls13_sha512:
+ return NamedGroup.brainpoolP512r1tls13;
+ case ecdsa_secp256r1_sha256:
+ return NamedGroup.secp256r1;
+ case ecdsa_secp384r1_sha384:
+ return NamedGroup.secp384r1;
+ case ecdsa_secp521r1_sha512:
+ return NamedGroup.secp521r1;
+ case sm2sig_sm3:
+ return NamedGroup.curveSM2;
+ default:
+ return -1;
+ }
+ }
+
+ public static short GetHashAlgorithm(int signatureScheme)
+ {
+ // TODO[RFC 8998] sm2sig_sm3
+ return (short)((signatureScheme >> 8) & 0xFF);
+ }
+
+ public static short GetSignatureAlgorithm(int signatureScheme)
+ {
+ // TODO[RFC 8998] sm2sig_sm3
+ return (short)(signatureScheme & 0xFF);
+ }
+
+ public static SignatureAndHashAlgorithm GetSignatureAndHashAlgorithm(int signatureScheme)
+ {
+ return SignatureAndHashAlgorithm.GetInstance(
+ GetHashAlgorithm(signatureScheme),
+ GetSignatureAlgorithm(signatureScheme));
+ }
+
+ public static string GetText(int signatureScheme)
+ {
+ string hex = Platform.ToUpperInvariant(Convert.ToString(signatureScheme, 16));
+ return GetName(signatureScheme) + "(0x" + hex + ")";
+ }
+
+ public static bool IsPrivate(int signatureScheme)
+ {
+ return (signatureScheme >> 9) == 0xFE;
+ }
+
+ public static bool IsECDsa(int signatureScheme)
+ {
+ switch (signatureScheme)
+ {
+ case ecdsa_brainpoolP256r1tls13_sha256:
+ case ecdsa_brainpoolP384r1tls13_sha384:
+ case ecdsa_brainpoolP512r1tls13_sha512:
+ return true;
+ default:
+ return SignatureAlgorithm.ecdsa == GetSignatureAlgorithm(signatureScheme);
+ }
+ }
+
+ public static bool IsRsaPss(int signatureScheme)
+ {
+ switch (signatureScheme)
+ {
+ case rsa_pss_rsae_sha256:
+ case rsa_pss_rsae_sha384:
+ case rsa_pss_rsae_sha512:
+ case rsa_pss_pss_sha256:
+ case rsa_pss_pss_sha384:
+ case rsa_pss_pss_sha512:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/SimulatedTlsSrpIdentityManager.cs b/crypto/src/tls/SimulatedTlsSrpIdentityManager.cs
new file mode 100644
index 000000000..904bdae6d
--- /dev/null
+++ b/crypto/src/tls/SimulatedTlsSrpIdentityManager.cs
@@ -0,0 +1,69 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>An implementation of <see cref="TlsSrpIdentityManager"/> that simulates the existence of "unknown"
+ /// identities to obscure the fact that there is no verifier for them.</summary>
+ public class SimulatedTlsSrpIdentityManager
+ : TlsSrpIdentityManager
+ {
+ private static readonly byte[] PrefixPassword = Strings.ToByteArray("password");
+ private static readonly byte[] PrefixSalt = Strings.ToByteArray("salt");
+
+ /// <summary>Create a <see cref="SimulatedTlsSrpIdentityManager"/> that implements the algorithm from RFC 5054
+ /// 2.5.1.3.</summary>
+ /// <param name="crypto"><see cref="TlsCrypto"/></param>
+ /// <param name="group">the <see cref="Srp6Group"/> defining the group that SRP is operating in.</param>
+ /// <param name="seedKey">the secret "seed key" referred to in RFC 5054 2.5.1.3.</param>
+ /// <returns>an instance of <see cref="SimulatedTlsSrpIdentityManager"/>.</returns>
+ /// <exception cref="IOException"/>
+ public static SimulatedTlsSrpIdentityManager GetRfc5054Default(TlsCrypto crypto, Srp6Group group, byte[] seedKey)
+ {
+ TlsMac mac = crypto.CreateHmac(MacAlgorithm.hmac_sha1);
+
+ mac.SetKey(seedKey, 0, seedKey.Length);
+
+ TlsSrpConfig srpConfig = new TlsSrpConfig();
+
+ srpConfig.SetExplicitNG(new BigInteger[]{ group.N, group.G });
+
+ return new SimulatedTlsSrpIdentityManager(group, crypto.CreateSrp6VerifierGenerator(srpConfig), mac);
+ }
+
+ protected readonly Srp6Group m_group;
+ protected readonly TlsSrp6VerifierGenerator m_verifierGenerator;
+ protected readonly TlsMac m_mac;
+
+ public SimulatedTlsSrpIdentityManager(Srp6Group group, TlsSrp6VerifierGenerator verifierGenerator, TlsMac mac)
+ {
+ this.m_group = group;
+ this.m_verifierGenerator = verifierGenerator;
+ this.m_mac = mac;
+ }
+
+ public virtual TlsSrpLoginParameters GetLoginParameters(byte[] identity)
+ {
+ m_mac.Update(PrefixSalt, 0, PrefixSalt.Length);
+ m_mac.Update(identity, 0, identity.Length);
+
+ byte[] salt = m_mac.CalculateMac();
+
+ m_mac.Update(PrefixPassword, 0, PrefixPassword.Length);
+ m_mac.Update(identity, 0, identity.Length);
+
+ byte[] password = m_mac.CalculateMac();
+
+ BigInteger verifier = m_verifierGenerator.GenerateVerifier(salt, identity, password);
+
+ TlsSrpConfig srpConfig = new TlsSrpConfig();
+ srpConfig.SetExplicitNG(new BigInteger[]{ m_group.N, m_group.G });
+
+ return new TlsSrpLoginParameters(identity, srpConfig, verifier, salt);
+ }
+ }
+}
diff --git a/crypto/src/tls/SrpTlsClient.cs b/crypto/src/tls/SrpTlsClient.cs
new file mode 100644
index 000000000..a2b0e9461
--- /dev/null
+++ b/crypto/src/tls/SrpTlsClient.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class SrpTlsClient
+ : AbstractTlsClient
+ {
+ private static readonly int[] DefaultCipherSuites = new int[]
+ {
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
+ };
+
+ protected readonly TlsSrpIdentity m_srpIdentity;
+
+ public SrpTlsClient(TlsCrypto crypto, byte[] identity, byte[] password)
+ : this(crypto, new BasicTlsSrpIdentity(identity, password))
+ {
+ }
+
+ public SrpTlsClient(TlsCrypto crypto, TlsSrpIdentity srpIdentity)
+ : base(crypto)
+ {
+ this.m_srpIdentity = srpIdentity;
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.DownTo(ProtocolVersion.TLSv10);
+ }
+
+ protected virtual bool RequireSrpServerExtension
+ {
+ // No explicit guidance in RFC 5054; by default an (empty) extension from server is optional
+ get { return false; }
+ }
+
+ /// <exception cref="IOException"/>
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+ TlsSrpUtilities.AddSrpExtension(clientExtensions, m_srpIdentity.GetSrpIdentity());
+ return clientExtensions;
+ }
+
+ /// <exception cref="IOException"/>
+ public override void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp,
+ AlertDescription.illegal_parameter))
+ {
+ if (RequireSrpServerExtension)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ base.ProcessServerExtensions(serverExtensions);
+ }
+
+ public override TlsSrpIdentity GetSrpIdentity()
+ {
+ return m_srpIdentity;
+ }
+
+ /// <exception cref="IOException"/>
+ public override TlsAuthentication GetAuthentication()
+ {
+ /*
+ * 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);
+ }
+ }
+}
diff --git a/crypto/src/tls/SrpTlsServer.cs b/crypto/src/tls/SrpTlsServer.cs
new file mode 100644
index 000000000..58f89ee22
--- /dev/null
+++ b/crypto/src/tls/SrpTlsServer.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class SrpTlsServer
+ : AbstractTlsServer
+ {
+ private static readonly int[] DefaultCipherSuites = 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
+ };
+
+ protected readonly TlsSrpIdentityManager m_srpIdentityManager;
+
+ protected byte[] m_srpIdentity = null;
+ protected TlsSrpLoginParameters m_srpLoginParameters = null;
+
+ public SrpTlsServer(TlsCrypto crypto, TlsSrpIdentityManager srpIdentityManager)
+ : base(crypto)
+ {
+ this.m_srpIdentityManager = srpIdentityManager;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedSigner GetDsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.DownTo(ProtocolVersion.TLSv10);
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
+ }
+
+ public override void ProcessClientExtensions(IDictionary clientExtensions)
+ {
+ base.ProcessClientExtensions(clientExtensions);
+
+ this.m_srpIdentity = TlsSrpUtilities.GetSrpExtension(clientExtensions);
+ }
+
+ public override int GetSelectedCipherSuite()
+ {
+ int cipherSuite = base.GetSelectedCipherSuite();
+
+ if (TlsSrpUtilities.IsSrpCipherSuite(cipherSuite))
+ {
+ if (m_srpIdentity != null)
+ {
+ this.m_srpLoginParameters = m_srpIdentityManager.GetLoginParameters(m_srpIdentity);
+ }
+
+ if (m_srpLoginParameters == null)
+ throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
+ }
+
+ return cipherSuite;
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ int keyExchangeAlgorithm = m_context.SecurityParameters.KeyExchangeAlgorithm;
+
+ 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 TlsSrpLoginParameters GetSrpLoginParameters()
+ {
+ return m_srpLoginParameters;
+ }
+ }
+}
diff --git a/crypto/src/tls/SrtpProtectionProfile.cs b/crypto/src/tls/SrtpProtectionProfile.cs
new file mode 100644
index 000000000..e81988e41
--- /dev/null
+++ b/crypto/src/tls/SrtpProtectionProfile.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class SrtpProtectionProfile
+ {
+ /*
+ * RFC 5764 4.1.2.
+ */
+ public const int SRTP_AES128_CM_HMAC_SHA1_80 = 0x0001;
+ public const int SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002;
+ public const int SRTP_NULL_HMAC_SHA1_80 = 0x0005;
+ public const int SRTP_NULL_HMAC_SHA1_32 = 0x0006;
+
+ /*
+ * RFC 7714 14.2.
+ */
+ public const int SRTP_AEAD_AES_128_GCM = 0x0007;
+ public const int SRTP_AEAD_AES_256_GCM = 0x0008;
+ }
+}
diff --git a/crypto/src/tls/Ssl3Utilities.cs b/crypto/src/tls/Ssl3Utilities.cs
new file mode 100644
index 000000000..594bdefbc
--- /dev/null
+++ b/crypto/src/tls/Ssl3Utilities.cs
@@ -0,0 +1,69 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal abstract class Ssl3Utilities
+ {
+ private static readonly byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54};
+ private static readonly byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52};
+
+ private const byte IPAD_BYTE = (byte)0x36;
+ private const byte OPAD_BYTE = (byte)0x5C;
+
+ private static readonly byte[] IPAD = GenPad(IPAD_BYTE, 48);
+ private static readonly byte[] OPAD = GenPad(OPAD_BYTE, 48);
+
+ internal static byte[] CalculateVerifyData(TlsHandshakeHash handshakeHash, bool isServer)
+ {
+ TlsHash prf = handshakeHash.ForkPrfHash();
+ byte[] sslSender = isServer ? SSL_SERVER : SSL_CLIENT;
+ prf.Update(sslSender, 0, sslSender.Length);
+ return prf.CalculateHash();
+ }
+
+ internal static void CompleteCombinedHash(TlsContext context, TlsHash md5, TlsHash sha1)
+ {
+ TlsSecret masterSecret = context.SecurityParameters.MasterSecret;
+ byte[] master_secret = context.Crypto.AdoptSecret(masterSecret).Extract();
+
+ CompleteHash(master_secret, md5, 48);
+ CompleteHash(master_secret, sha1, 40);
+ }
+
+ private static void CompleteHash(byte[] master_secret, TlsHash hash, int padLength)
+ {
+ hash.Update(master_secret, 0, master_secret.Length);
+ hash.Update(IPAD, 0, padLength);
+
+ byte[] tmp = hash.CalculateHash();
+
+ hash.Update(master_secret, 0, master_secret.Length);
+ hash.Update(OPAD, 0, padLength);
+ hash.Update(tmp, 0, tmp.Length);
+ }
+
+ private static byte[] GenPad(byte b, int count)
+ {
+ byte[] padding = new byte[count];
+ Arrays.Fill(padding, b);
+ return padding;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] ReadEncryptedPms(Stream input)
+ {
+ return Streams.ReadAll(input);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void WriteEncryptedPms(byte[] encryptedPms, Stream output)
+ {
+ output.Write(encryptedPms, 0, encryptedPms.Length);
+ }
+ }
+}
diff --git a/crypto/src/tls/SupplementalDataEntry.cs b/crypto/src/tls/SupplementalDataEntry.cs
new file mode 100644
index 000000000..baf428968
--- /dev/null
+++ b/crypto/src/tls/SupplementalDataEntry.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class SupplementalDataEntry
+ {
+ private readonly int m_dataType;
+ private readonly byte[] m_data;
+
+ public SupplementalDataEntry(int dataType, byte[] data)
+ {
+ this.m_dataType = dataType;
+ this.m_data = data;
+ }
+
+ public int DataType
+ {
+ get { return m_dataType; }
+ }
+
+ public byte[] Data
+ {
+ get { return m_data; }
+ }
+ }
+}
diff --git a/crypto/src/tls/SupplementalDataType.cs b/crypto/src/tls/SupplementalDataType.cs
new file mode 100644
index 000000000..d1eebfdc1
--- /dev/null
+++ b/crypto/src/tls/SupplementalDataType.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 4680</summary>
+ public abstract class SupplementalDataType
+ {
+ /*
+ * RFC 4681
+ */
+ public const int user_mapping_data = 0;
+ }
+}
diff --git a/crypto/src/tls/Timeout.cs b/crypto/src/tls/Timeout.cs
new file mode 100644
index 000000000..e47fc5d9a
--- /dev/null
+++ b/crypto/src/tls/Timeout.cs
@@ -0,0 +1,119 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class Timeout
+ {
+ private long durationMillis;
+ private long startMillis;
+
+ internal Timeout(long durationMillis)
+ : this(durationMillis, DateTimeUtilities.CurrentUnixMs())
+ {
+ }
+
+ internal Timeout(long durationMillis, long currentTimeMillis)
+ {
+ this.durationMillis = System.Math.Max(0, durationMillis);
+ this.startMillis = System.Math.Max(0, currentTimeMillis);
+ }
+
+ //internal long RemainingMillis()
+ //{
+ // return RemainingMillis(DateTimeUtilities.CurrentUnixMs());
+ //}
+
+ internal long RemainingMillis(long currentTimeMillis)
+ {
+ lock (this)
+ {
+ // If clock jumped backwards, reset start time
+ if (startMillis > currentTimeMillis)
+ {
+ startMillis = currentTimeMillis;
+ return durationMillis;
+ }
+
+ long elapsed = currentTimeMillis - startMillis;
+ long remaining = durationMillis - elapsed;
+
+ // Once timeout reached, lock it in
+ if (remaining <= 0)
+ return durationMillis = 0L;
+
+ return remaining;
+ }
+ }
+
+ //internal static int ConstrainWaitMillis(int waitMillis, Timeout timeout)
+ //{
+ // return constrainWaitMillis(waitMillis, timeout, DateTimeUtilities.CurrentUnixMs());
+ //}
+
+ internal static int ConstrainWaitMillis(int waitMillis, Timeout timeout, long currentTimeMillis)
+ {
+ if (waitMillis < 0)
+ return -1;
+
+ int timeoutMillis = GetWaitMillis(timeout, currentTimeMillis);
+ if (timeoutMillis < 0)
+ return -1;
+
+ if (waitMillis == 0)
+ return timeoutMillis;
+
+ if (timeoutMillis == 0)
+ return waitMillis;
+
+ return System.Math.Min(waitMillis, timeoutMillis);
+ }
+
+ internal static Timeout ForWaitMillis(int waitMillis)
+ {
+ return ForWaitMillis(waitMillis, DateTimeUtilities.CurrentUnixMs());
+ }
+
+ internal static Timeout ForWaitMillis(int waitMillis, long currentTimeMillis)
+ {
+ if (waitMillis < 0)
+ throw new ArgumentException("cannot be negative", "waitMillis");
+
+ if (waitMillis > 0)
+ return new Timeout(waitMillis, currentTimeMillis);
+
+ return null;
+ }
+
+ //internal static int GetWaitMillis(Timeout timeout)
+ //{
+ // return GetWaitMillis(timeout, DateTimeUtilities.CurrentUnixMs());
+ //}
+
+ internal static int GetWaitMillis(Timeout timeout, long currentTimeMillis)
+ {
+ if (null == timeout)
+ return 0;
+
+ long remainingMillis = timeout.RemainingMillis(currentTimeMillis);
+ if (remainingMillis < 1L)
+ return -1;
+
+ if (remainingMillis > int.MaxValue)
+ return int.MaxValue;
+
+ return (int)remainingMillis;
+ }
+
+ internal static bool HasExpired(Timeout timeout)
+ {
+ return HasExpired(timeout, DateTimeUtilities.CurrentUnixMs());
+ }
+
+ internal static bool HasExpired(Timeout timeout, long currentTimeMillis)
+ {
+ return null != timeout && timeout.RemainingMillis(currentTimeMillis) < 1L;
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsAuthentication.cs b/crypto/src/tls/TlsAuthentication.cs
new file mode 100644
index 000000000..32228ed64
--- /dev/null
+++ b/crypto/src/tls/TlsAuthentication.cs
@@ -0,0 +1,29 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface to provide TLS authentication credentials.</summary>
+ public interface TlsAuthentication
+ {
+ /// <summary>Called by the protocol handler to report the server certificate.</summary>
+ /// <remarks>
+ /// Note: this method is responsible for certificate verification and validation.
+ /// </remarks>
+ /// <param name="serverCertificate">the server certificate received.</param>
+ /// <exception cref="IOException"/>
+ void NotifyServerCertificate(TlsServerCertificate serverCertificate);
+
+ /// <summary>Return client credentials in response to server's certificate request.</summary>
+ /// <remarks>
+ /// The returned value may be null, or else it MUST implement <em>exactly one</em> of
+ /// <see cref="TlsCredentialedAgreement"/>, <see cref="TlsCredentialedDecryptor"/>, or
+ /// <see cref="TlsCredentialedSigner"/>, depending on the key exchange that was negotiated and the details of
+ /// the <see cref="CertificateRequest"/>.
+ /// </remarks>
+ /// <param name="certificateRequest">details of the certificate request.</param>
+ /// <returns>a <see cref="TlsCredentials"/> object or null for no client authentication.</returns>
+ /// <exception cref="IOException"/>
+ TlsCredentials GetClientCredentials(CertificateRequest certificateRequest);
+ }
+}
diff --git a/crypto/src/tls/TlsClient.cs b/crypto/src/tls/TlsClient.cs
new file mode 100644
index 000000000..4d2e15437
--- /dev/null
+++ b/crypto/src/tls/TlsClient.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public interface TlsClient
+ : TlsPeer
+ {
+ 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>
+ /// <seealso cref="SessionParameters.PeerCertificate"/>
+ TlsSession GetSessionToResume();
+
+ bool IsFallback();
+
+ /// <returns>(Int32 -> byte[])</returns>
+ /// <exception cref="IOException"/>
+ IDictionary GetClientExtensions();
+
+ /// <summary>If this client is offering TLS 1.3 or higher, this method may be called to determine for which
+ /// groups a key share should be included in the initial ClientHello.</summary>
+ /// <remarks>
+ /// Groups that were not included in the supported_groups extension (by <see cref="GetClientExtensions"/> will
+ /// be ignored. The protocol will then add a suitable key_share extension to the ClientHello extensions.
+ /// </remarks>
+ /// <returns>an <see cref="IList"/> of <see cref="NamedGroup">named group</see> values, possibly empty or null.
+ /// </returns>
+ IList GetEarlyKeyShareGroups();
+
+ /// <exception cref="IOException"/>
+ void NotifyServerVersion(ProtocolVersion selectedVersion);
+
+ /// <summary>Notifies the client of the session that will be offered in ClientHello for resumption, if any.
+ /// </summary>
+ /// <remarks>
+ /// This will be either the session returned from {@link #getSessionToResume()} or null if that session was
+ /// unusable. NOTE: the actual negotiated session_id is notified by <see cref="NotifySessionID(byte[])"/>.
+ /// </remarks>
+ /// <param name="session">The <see cref="TlsSession"/> representing the resumable session to be offered for
+ /// this connection, or null if there is none.</param>
+ /// <seealso cref="NotifySessionID(byte[])"/>
+ void NotifySessionToResume(TlsSession session);
+
+ /// <summary>Notifies the client of the session_id sent in the ServerHello.</summary>
+ /// <param name="sessionID"/>
+ /// <seealso cref="TlsContext.Session"/>
+ void NotifySessionID(byte[] sessionID);
+
+ void NotifySelectedCipherSuite(int selectedCipherSuite);
+
+ /// <summary>The protocol implementation validates that any server extensions received correspond to client
+ /// extensions sent.</summary>
+ /// <remarks>
+ /// If further processing of the server extensions is needed, it can be done in this callback. NOTE: This is
+ /// not called for session resumption handshakes.
+ /// </remarks>
+ /// <param name="serverExtensions">(Int32 -> byte[])</param>
+ /// <exception cref="IOException"/>
+ void ProcessServerExtensions(IDictionary serverExtensions);
+
+ /// <param name="serverSupplementalData">(SupplementalDataEntry)</param>
+ /// <exception cref="IOException"/>
+ void ProcessServerSupplementalData(IList serverSupplementalData);
+
+ /// <exception cref="IOException"/>
+ TlsPskIdentity GetPskIdentity();
+
+ /// <exception cref="IOException"/>
+ TlsSrpIdentity GetSrpIdentity();
+
+ /// <exception cref="IOException"/>
+ TlsDHGroupVerifier GetDHGroupVerifier();
+
+ /// <exception cref="IOException"/>
+ TlsSrpConfigVerifier GetSrpConfigVerifier();
+
+ /// <exception cref="IOException"/>
+ TlsAuthentication GetAuthentication();
+
+ /// <returns>(SupplementalDataEntry)</returns>
+ /// <exception cref="IOException"/>
+ IList GetClientSupplementalData();
+
+ /// <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.
+ /// RFC 5077 4. "Recommended Ticket Construction".
+ /// </remarks>
+ /// <param name="newSessionTicket">The ticket.</param>
+ /// <exception cref="IOException"/>
+ void NotifyNewSessionTicket(NewSessionTicket newSessionTicket);
+ }
+}
diff --git a/crypto/src/tls/TlsClientContext.cs b/crypto/src/tls/TlsClientContext.cs
new file mode 100644
index 000000000..f1ed06e04
--- /dev/null
+++ b/crypto/src/tls/TlsClientContext.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Marker interface to distinguish a TLS client context.</summary>
+ public interface TlsClientContext
+ : TlsContext
+ {
+ }
+}
diff --git a/crypto/src/tls/TlsClientContextImpl.cs b/crypto/src/tls/TlsClientContextImpl.cs
new file mode 100644
index 000000000..3ce4ab86b
--- /dev/null
+++ b/crypto/src/tls/TlsClientContextImpl.cs
@@ -0,0 +1,20 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class TlsClientContextImpl
+ : AbstractTlsContext, TlsClientContext
+ {
+ internal TlsClientContextImpl(TlsCrypto crypto)
+ : base(crypto, ConnectionEnd.client)
+ {
+ }
+
+ public override bool IsServer
+ {
+ get { return false; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsClientProtocol.cs b/crypto/src/tls/TlsClientProtocol.cs
new file mode 100644
index 000000000..31ad9b956
--- /dev/null
+++ b/crypto/src/tls/TlsClientProtocol.cs
@@ -0,0 +1,1715 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsClientProtocol
+ : TlsProtocol
+ {
+ protected TlsClient m_tlsClient = null;
+ internal TlsClientContextImpl m_tlsClientContext = null;
+
+ protected IDictionary m_clientAgreements = null;
+ protected ClientHello m_clientHello = null;
+ protected TlsKeyExchange m_keyExchange = null;
+ protected TlsAuthentication m_authentication = null;
+
+ protected CertificateStatus m_certificateStatus = null;
+ protected CertificateRequest m_certificateRequest = null;
+
+ /// <summary>Constructor for non-blocking mode.</summary>
+ /// <remarks>
+ /// When data is received, use <see cref="TlsProtocol.OfferInput(byte[])"/> to provide the received ciphertext,
+ /// then use <see cref="TlsProtocol.ReadInput(byte[],int,int)"/> to read the corresponding cleartext.<br/><br/>
+ /// Similarly, when data needs to be sent, use <see cref="TlsProtocol.WriteApplicationData(byte[],int,int)"/>
+ /// to provide the cleartext, then use <see cref="TlsProtocol.ReadOutput(byte[],int,int)"/> to get the
+ /// corresponding ciphertext.
+ /// </remarks>
+ public TlsClientProtocol()
+ : base()
+ {
+ }
+
+ /// <summary>Constructor for blocking mode.</summary>
+ /// <param name="stream">The <see cref="Stream"/> of data to/from the server.</param>
+ public TlsClientProtocol(Stream stream)
+ : base(stream)
+ {
+ }
+
+ /// <summary>Constructor for blocking mode.</summary>
+ /// <param name="input">The <see cref="Stream"/> of data from the server.</param>
+ /// <param name="output">The <see cref="Stream"/> of data to the server.</param>
+ public TlsClientProtocol(Stream input, Stream output)
+ : base(input, output)
+ {
+ }
+
+ /// <summary>Initiates a TLS handshake in the role of client.</summary>
+ /// <remarks>
+ /// In blocking mode, this will not return until the handshake is complete. In non-blocking mode, use
+ /// <see cref="TlsPeer.NotifyHandshakeComplete"/> to receive a callback when the handshake is complete.
+ /// </remarks>
+ /// <param name="tlsClient">The <see cref="TlsClient"/> to use for the handshake.</param>
+ /// <exception cref="IOException">If in blocking mode and handshake was not successful.</exception>
+ public virtual void Connect(TlsClient tlsClient)
+ {
+ if (tlsClient == null)
+ throw new ArgumentNullException("tlsClient");
+ if (m_tlsClient != null)
+ throw new InvalidOperationException("'Connect' can only be called once");
+
+ this.m_tlsClient = tlsClient;
+ this.m_tlsClientContext = new TlsClientContextImpl(tlsClient.Crypto);
+
+ tlsClient.Init(m_tlsClientContext);
+ tlsClient.NotifyCloseHandle(this);
+
+ BeginHandshake();
+
+ if (m_blocking)
+ {
+ BlockForHandshake();
+ }
+ }
+
+ protected override void BeginHandshake()
+ {
+ base.BeginHandshake();
+
+ EstablishSession(m_tlsClient.GetSessionToResume());
+ m_tlsClient.NotifySessionToResume(m_tlsSession);
+
+ SendClientHello();
+ this.m_connectionState = CS_CLIENT_HELLO;
+ }
+
+ protected override void CleanupHandshake()
+ {
+ base.CleanupHandshake();
+
+ this.m_clientAgreements = null;
+ this.m_clientHello = null;
+ this.m_keyExchange = null;
+ this.m_authentication = null;
+
+ this.m_certificateStatus = null;
+ this.m_certificateRequest = null;
+ }
+
+ protected override TlsContext Context
+ {
+ get { return m_tlsClientContext; }
+ }
+
+ internal override AbstractTlsContext ContextAdmin
+ {
+ get { return m_tlsClientContext; }
+ }
+
+ protected override TlsPeer Peer
+ {
+ get { return m_tlsClient; }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Handle13HandshakeMessage(short type, HandshakeMessageInput buf)
+ {
+ if (!IsTlsV13ConnectionState())
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (m_resumedSession)
+ {
+ /*
+ * TODO[tls13] Resumption/PSK
+ *
+ * NOTE: No CertificateRequest, Certificate, CertificateVerify messages, but client
+ * might now send EndOfEarlyData after receiving server Finished message.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_ENCRYPTED_EXTENSIONS:
+ case CS_SERVER_CERTIFICATE_REQUEST:
+ {
+ if (m_connectionState != CS_SERVER_CERTIFICATE_REQUEST)
+ {
+ Skip13CertificateRequest();
+ }
+
+ /*
+ * TODO[tls13] For PSK-only key exchange, there's no Certificate message.
+ */
+ Receive13ServerCertificate(buf);
+ this.m_connectionState = CS_SERVER_CERTIFICATE;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate_request:
+ {
+ switch (m_connectionState)
+ {
+ case CS_END:
+ {
+ // TODO[tls13] Permit post-handshake authentication if we sent post_handshake_auth extension
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ case CS_SERVER_ENCRYPTED_EXTENSIONS:
+ {
+ Receive13CertificateRequest(buf, false);
+ this.m_connectionState = CS_SERVER_CERTIFICATE_REQUEST;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate_verify:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ {
+ Receive13ServerCertificateVerify(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_SERVER_CERTIFICATE_VERIFY;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.encrypted_extensions:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO:
+ {
+ Receive13EncryptedExtensions(buf);
+ this.m_connectionState = CS_SERVER_ENCRYPTED_EXTENSIONS;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_ENCRYPTED_EXTENSIONS:
+ case CS_SERVER_CERTIFICATE_REQUEST:
+ case CS_SERVER_CERTIFICATE_VERIFY:
+ {
+ if (m_connectionState == CS_SERVER_ENCRYPTED_EXTENSIONS)
+ {
+ Skip13CertificateRequest();
+ }
+ if (m_connectionState != CS_SERVER_CERTIFICATE_VERIFY)
+ {
+ Skip13ServerCertificate();
+ }
+
+ Receive13ServerFinished(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_SERVER_FINISHED;
+
+ byte[] serverFinishedTranscriptHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
+
+ // See RFC 8446 D.4.
+ m_recordStream.SetIgnoreChangeCipherSpec(false);
+
+ if (null != m_certificateRequest)
+ {
+ TlsCredentialedSigner clientCredentials = TlsUtilities.Establish13ClientCredentials(
+ m_authentication, m_certificateRequest);
+
+ Certificate clientCertificate = null;
+ if (null != clientCredentials)
+ {
+ clientCertificate = clientCredentials.Certificate;
+ }
+
+ if (null == clientCertificate)
+ {
+ // In this calling context, certificate_request_context is length 0
+ clientCertificate = Certificate.EmptyChainTls13;
+ }
+
+ Send13CertificateMessage(clientCertificate);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE;
+
+ if (null != clientCredentials)
+ {
+ DigitallySigned certificateVerify = TlsUtilities.Generate13CertificateVerify(
+ m_tlsClientContext, clientCredentials, m_handshakeHash);
+ Send13CertificateVerifyMessage(certificateVerify);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
+ }
+ }
+
+ Send13FinishedMessage();
+ this.m_connectionState = CS_CLIENT_FINISHED;
+
+ TlsUtilities.Establish13PhaseApplication(m_tlsClientContext, serverFinishedTranscriptHash,
+ m_recordStream);
+
+ m_recordStream.EnablePendingCipherWrite();
+ m_recordStream.EnablePendingCipherRead(false);
+
+ CompleteHandshake();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.key_update:
+ {
+ Receive13KeyUpdate(buf);
+ break;
+ }
+ case HandshakeType.new_session_ticket:
+ {
+ Receive13NewSessionTicket(buf);
+ break;
+ }
+ case HandshakeType.server_hello:
+ {
+ switch (m_connectionState)
+ {
+ case CS_CLIENT_HELLO:
+ {
+ // NOTE: Legacy handler should be dispatching initial ServerHello/HelloRetryRequest.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ case CS_CLIENT_HELLO_RETRY:
+ {
+ ServerHello serverHello = ReceiveServerHelloMessage(buf);
+ if (serverHello.IsHelloRetryRequest())
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ Process13ServerHello(serverHello, true);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_SERVER_HELLO;
+
+ Process13ServerHelloCoda(serverHello, true);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+
+ case HandshakeType.certificate_status:
+ case HandshakeType.certificate_url:
+ case HandshakeType.client_hello:
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.end_of_early_data:
+ case HandshakeType.hello_request:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.message_hash:
+ case HandshakeType.server_hello_done:
+ case HandshakeType.server_key_exchange:
+ case HandshakeType.supplemental_data:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ protected override void HandleHandshakeMessage(short type, HandshakeMessageInput buf)
+ {
+ SecurityParameters securityParameters = m_tlsClientContext.SecurityParameters;
+
+ if (m_connectionState > CS_CLIENT_HELLO
+ && TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
+ {
+ Handle13HandshakeMessage(type, buf);
+ return;
+ }
+
+ if (!IsLegacyConnectionState())
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (m_resumedSession)
+ {
+ if (type != HandshakeType.finished || m_connectionState != CS_SERVER_HELLO)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ProcessFinishedMessage(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_SERVER_FINISHED;
+
+ SendChangeCipherSpec();
+ SendFinishedMessage();
+ this.m_connectionState = CS_CLIENT_FINISHED;
+
+ CompleteHandshake();
+ return;
+ }
+
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ {
+ if (m_connectionState != CS_SERVER_SUPPLEMENTAL_DATA)
+ {
+ HandleSupplementalData(null);
+ }
+
+ /*
+ * NOTE: Certificate processing (including authentication) is delayed to allow for a
+ * possible CertificateStatus message.
+ */
+ this.m_authentication = TlsUtilities.ReceiveServerCertificate(m_tlsClientContext, m_tlsClient,
+ buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.m_connectionState = CS_SERVER_CERTIFICATE;
+ break;
+ }
+ case HandshakeType.certificate_status:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ {
+ if (securityParameters.StatusRequestVersion < 1)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ this.m_certificateStatus = CertificateStatus.Parse(m_tlsClientContext, buf);
+
+ AssertEmpty(buf);
+
+ this.m_connectionState = CS_SERVER_CERTIFICATE_STATUS;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (m_connectionState)
+ {
+ case CS_CLIENT_FINISHED:
+ case CS_SERVER_SESSION_TICKET:
+ {
+ if (m_connectionState != CS_SERVER_SESSION_TICKET)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST be sent if the server included a
+ * SessionTicket extension in the ServerHello.
+ */
+ if (m_expectSessionTicket)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ ProcessFinishedMessage(buf);
+ this.m_connectionState = CS_SERVER_FINISHED;
+
+ CompleteHandshake();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello:
+ {
+ switch (m_connectionState)
+ {
+ case CS_CLIENT_HELLO:
+ {
+ ServerHello serverHello = ReceiveServerHelloMessage(buf);
+
+ // TODO[tls13] Only treat as HRR if it's TLS 1.3??
+ if (serverHello.IsHelloRetryRequest())
+ {
+ Process13HelloRetryRequest(serverHello);
+ m_handshakeHash.NotifyPrfDetermined();
+ TlsUtilities.AdjustTranscriptForRetry(m_handshakeHash);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_SERVER_HELLO_RETRY_REQUEST;
+
+ Send13ClientHelloRetry();
+ this.m_connectionState = CS_CLIENT_HELLO_RETRY;
+ }
+ else
+ {
+ ProcessServerHello(serverHello);
+ m_handshakeHash.NotifyPrfDetermined();
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_SERVER_HELLO;
+
+ if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
+ {
+ Process13ServerHelloCoda(serverHello, false);
+ }
+ }
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.supplemental_data:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO:
+ {
+ HandleSupplementalData(ReadSupplementalDataMessage(buf));
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello_done:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_SERVER_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ case CS_SERVER_CERTIFICATE_REQUEST:
+ {
+ if (m_connectionState == CS_SERVER_HELLO)
+ {
+ HandleSupplementalData(null);
+ }
+ if (m_connectionState == CS_SERVER_HELLO ||
+ m_connectionState == CS_SERVER_SUPPLEMENTAL_DATA)
+ {
+ this.m_authentication = null;
+ }
+ if (m_connectionState != CS_SERVER_KEY_EXCHANGE &&
+ m_connectionState != CS_SERVER_CERTIFICATE_REQUEST)
+ {
+ HandleServerCertificate();
+
+ // There was no server key exchange message; check it's OK
+ m_keyExchange.SkipServerKeyExchange();
+ }
+
+ AssertEmpty(buf);
+
+ this.m_connectionState = CS_SERVER_HELLO_DONE;
+
+ IList clientSupplementalData = m_tlsClient.GetClientSupplementalData();
+ if (clientSupplementalData != null)
+ {
+ SendSupplementalDataMessage(clientSupplementalData);
+ this.m_connectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
+ }
+
+ TlsCredentialedSigner credentialedSigner = null;
+ TlsStreamSigner streamSigner = null;
+
+ if (m_certificateRequest == null)
+ {
+ m_keyExchange.SkipClientCredentials();
+ }
+ else
+ {
+ Certificate clientCertificate = null;
+
+ TlsCredentials clientCredentials = TlsUtilities.EstablishClientCredentials(m_authentication,
+ m_certificateRequest);
+ if (null == clientCredentials)
+ {
+ m_keyExchange.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.
+ */
+ }
+ else
+ {
+ m_keyExchange.ProcessClientCredentials(clientCredentials);
+
+ clientCertificate = clientCredentials.Certificate;
+
+ if (clientCredentials is TlsCredentialedSigner)
+ {
+ credentialedSigner = (TlsCredentialedSigner)clientCredentials;
+ streamSigner = credentialedSigner.GetStreamSigner();
+ }
+ }
+
+ SendCertificateMessage(clientCertificate, null);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE;
+ }
+
+ bool forceBuffering = streamSigner != null;
+ TlsUtilities.SealHandshakeHash(m_tlsClientContext, m_handshakeHash, forceBuffering);
+
+ /*
+ * Send the client key exchange message, depending on the key exchange we are using
+ * in our CipherSuite.
+ */
+ SendClientKeyExchange();
+ this.m_connectionState = CS_CLIENT_KEY_EXCHANGE;
+
+ bool isSsl = TlsUtilities.IsSsl(m_tlsClientContext);
+ if (isSsl)
+ {
+ // NOTE: For SSLv3 (only), master_secret needed to calculate session hash
+ EstablishMasterSecret(m_tlsClientContext, m_keyExchange);
+ }
+
+ securityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
+
+ if (!isSsl)
+ {
+ // NOTE: For (D)TLS, session hash potentially needed for extended_master_secret
+ EstablishMasterSecret(m_tlsClientContext, m_keyExchange);
+ }
+
+ m_recordStream.SetPendingCipher(TlsUtilities.InitCipher(m_tlsClientContext));
+
+ if (credentialedSigner != null)
+ {
+ DigitallySigned certificateVerify = TlsUtilities.GenerateCertificateVerifyClient(
+ m_tlsClientContext, credentialedSigner, streamSigner, m_handshakeHash);
+ SendCertificateVerifyMessage(certificateVerify);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
+ }
+
+ this.m_handshakeHash = m_handshakeHash.StopTracking();
+
+ SendChangeCipherSpec();
+ SendFinishedMessage();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.m_connectionState = CS_CLIENT_FINISHED;
+ break;
+ }
+ case HandshakeType.server_key_exchange:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_SERVER_CERTIFICATE_STATUS:
+ {
+ if (m_connectionState == CS_SERVER_HELLO)
+ {
+ HandleSupplementalData(null);
+ }
+ if (m_connectionState != CS_SERVER_CERTIFICATE &&
+ m_connectionState != CS_SERVER_CERTIFICATE_STATUS)
+ {
+ this.m_authentication = null;
+ }
+
+ HandleServerCertificate();
+
+ m_keyExchange.ProcessServerKeyExchange(buf);
+
+ AssertEmpty(buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.m_connectionState = CS_SERVER_KEY_EXCHANGE;
+ break;
+ }
+ case HandshakeType.certificate_request:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ case CS_SERVER_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ {
+ if (m_connectionState != CS_SERVER_KEY_EXCHANGE)
+ {
+ HandleServerCertificate();
+
+ // There was no server key exchange message; check it's OK
+ m_keyExchange.SkipServerKeyExchange();
+ }
+
+ ReceiveCertificateRequest(buf);
+
+ TlsUtilities.EstablishServerSigAlgs(securityParameters, m_certificateRequest);
+
+ /*
+ * TODO Give the client a chance to immediately select the CertificateVerify hash
+ * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+ */
+ TlsUtilities.TrackHashAlgorithms(m_handshakeHash, securityParameters.ServerSigAlgs);
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.m_connectionState = CS_SERVER_CERTIFICATE_REQUEST;
+ break;
+ }
+ case HandshakeType.new_session_ticket:
+ {
+ switch (m_connectionState)
+ {
+ case CS_CLIENT_FINISHED:
+ {
+ if (!m_expectSessionTicket)
+ {
+ /*
+ * 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();
+
+ ReceiveNewSessionTicket(buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.m_connectionState = 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 (IsApplicationDataReady)
+ {
+ RefuseRenegotiation();
+ }
+ break;
+ }
+
+ case HandshakeType.certificate_url:
+ case HandshakeType.certificate_verify:
+ case HandshakeType.client_hello:
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.encrypted_extensions:
+ case HandshakeType.end_of_early_data:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.key_update:
+ case HandshakeType.message_hash:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleServerCertificate()
+ {
+ TlsUtilities.ProcessServerCertificate(m_tlsClientContext, m_certificateStatus, m_keyExchange,
+ m_authentication, m_clientExtensions, m_serverExtensions);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleSupplementalData(IList serverSupplementalData)
+ {
+ m_tlsClient.ProcessServerSupplementalData(serverSupplementalData);
+ this.m_connectionState = CS_SERVER_SUPPLEMENTAL_DATA;
+
+ this.m_keyExchange = TlsUtilities.InitKeyExchangeClient(m_tlsClientContext, m_tlsClient);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Process13HelloRetryRequest(ServerHello helloRetryRequest)
+ {
+ ProtocolVersion legacy_record_version = ProtocolVersion.TLSv12;
+ m_recordStream.SetWriteVersion(legacy_record_version);
+
+ SecurityParameters securityParameters = m_tlsClientContext.SecurityParameters;
+
+ /*
+ * RFC 8446 4.1.4. Upon receipt of a HelloRetryRequest, the client MUST check the
+ * legacy_version, legacy_session_id_echo, cipher_suite, and legacy_compression_method as
+ * specified in Section 4.1.3 and then process the extensions, starting with determining the
+ * version using "supported_versions".
+ */
+ ProtocolVersion legacy_version = helloRetryRequest.Version;
+ byte[] legacy_session_id_echo = helloRetryRequest.SessionID;
+ int cipherSuite = helloRetryRequest.CipherSuite;
+ // NOTE: legacy_compression_method checked during ServerHello parsing
+
+ if (!ProtocolVersion.TLSv12.Equals(legacy_version) ||
+ !Arrays.AreEqual(m_clientHello.SessionID, legacy_session_id_echo) ||
+ !TlsUtilities.IsValidCipherSuiteSelection(m_clientHello.CipherSuites, cipherSuite))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ IDictionary extensions = helloRetryRequest.Extensions;
+ if (null == extensions)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ TlsUtilities.CheckExtensionData13(extensions, HandshakeType.hello_retry_request,
+ AlertDescription.illegal_parameter);
+
+ {
+ /*
+ * RFC 8446 4.2. Implementations MUST NOT send extension responses if the remote
+ * endpoint did not send the corresponding extension requests, with the exception of the
+ * "cookie" extension in the HelloRetryRequest. Upon receiving such an extension, an
+ * endpoint MUST abort the handshake with an "unsupported_extension" alert.
+ */
+ foreach (int extType in extensions.Keys)
+ {
+ if (ExtensionType.cookie == extType)
+ continue;
+
+ if (null == TlsUtilities.GetExtensionData(m_clientExtensions, extType))
+ throw new TlsFatalAlert(AlertDescription.unsupported_extension);
+ }
+ }
+
+ ProtocolVersion server_version = TlsExtensionsUtilities.GetSupportedVersionsExtensionServer(extensions);
+ if (null == server_version)
+ throw new TlsFatalAlert(AlertDescription.missing_extension);
+
+ if (!ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(server_version) ||
+ !ProtocolVersion.Contains(m_tlsClientContext.ClientSupportedVersions, server_version) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, server_version))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * RFC 8446 4.2.8. Upon receipt of this [Key Share] extension in a HelloRetryRequest, the
+ * client MUST verify that (1) the selected_group field corresponds to a group which was
+ * provided in the "supported_groups" extension in the original ClientHello and (2) the
+ * selected_group field does not correspond to a group which was provided in the "key_share"
+ * extension in the original ClientHello. If either of these checks fails, then the client
+ * MUST abort the handshake with an "illegal_parameter" alert.
+ */
+ int selected_group = TlsExtensionsUtilities.GetKeyShareHelloRetryRequest(extensions);
+
+ if (!TlsUtilities.IsValidKeyShareSelection(server_version, securityParameters.ClientSupportedGroups,
+ m_clientAgreements, selected_group))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ byte[] cookie = TlsExtensionsUtilities.GetCookieExtension(extensions);
+
+
+
+ securityParameters.m_negotiatedVersion = server_version;
+ TlsUtilities.NegotiatedVersionTlsClient(m_tlsClientContext, m_tlsClient);
+
+ this.m_resumedSession = false;
+ securityParameters.m_sessionID = TlsUtilities.EmptyBytes;
+ m_tlsClient.NotifySessionID(TlsUtilities.EmptyBytes);
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ m_tlsClient.NotifySelectedCipherSuite(cipherSuite);
+
+ this.m_clientAgreements = null;
+ this.m_retryCookie = cookie;
+ this.m_retryGroup = selected_group;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Process13ServerHello(ServerHello serverHello, bool afterHelloRetryRequest)
+ {
+ SecurityParameters securityParameters = m_tlsClientContext.SecurityParameters;
+
+ ProtocolVersion legacy_version = serverHello.Version;
+ byte[] legacy_session_id_echo = serverHello.SessionID;
+ int cipherSuite = serverHello.CipherSuite;
+ // NOTE: legacy_compression_method checked during ServerHello parsing
+
+ if (!ProtocolVersion.TLSv12.Equals(legacy_version) ||
+ !Arrays.AreEqual(m_clientHello.SessionID, legacy_session_id_echo))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ IDictionary extensions = serverHello.Extensions;
+ if (null == extensions)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ TlsUtilities.CheckExtensionData13(extensions, HandshakeType.server_hello,
+ AlertDescription.illegal_parameter);
+
+ if (afterHelloRetryRequest)
+ {
+ ProtocolVersion server_version = TlsExtensionsUtilities.GetSupportedVersionsExtensionServer(extensions);
+ if (null == server_version)
+ throw new TlsFatalAlert(AlertDescription.missing_extension);
+
+ if (!securityParameters.NegotiatedVersion.Equals(server_version) ||
+ securityParameters.CipherSuite != cipherSuite)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ else
+ {
+ if (!TlsUtilities.IsValidCipherSuiteSelection(m_clientHello.CipherSuites, cipherSuite) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.m_resumedSession = false;
+ securityParameters.m_sessionID = TlsUtilities.EmptyBytes;
+ m_tlsClient.NotifySessionID(TlsUtilities.EmptyBytes);
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ m_tlsClient.NotifySelectedCipherSuite(cipherSuite);
+ }
+
+ this.m_clientHello = null;
+
+ // NOTE: Apparently downgrade marker mechanism not used for TLS 1.3+?
+ securityParameters.m_serverRandom = serverHello.Random;
+
+ securityParameters.m_secureRenegotiation = false;
+
+ /*
+ * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
+ * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
+ * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
+ */
+ securityParameters.m_extendedMasterSecret = true;
+
+ /*
+ * TODO[tls13] RFC 8446 4.4.2.1. OCSP Status and SCT Extensions.
+ *
+ * OCSP information is carried in an extension for a CertificateEntry.
+ */
+ securityParameters.m_statusRequestVersion = m_clientExtensions.Contains(ExtensionType.status_request) ? 1 : 0;
+
+ {
+ KeyShareEntry keyShareEntry = TlsExtensionsUtilities.GetKeyShareServerHello(extensions);
+ if (null == keyShareEntry)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ if (!m_clientAgreements.Contains(keyShareEntry.NamedGroup))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ TlsAgreement agreement = (TlsAgreement)m_clientAgreements[keyShareEntry.NamedGroup];
+
+ this.m_clientAgreements = null;
+
+ agreement.ReceivePeerValue(keyShareEntry.KeyExchange);
+ securityParameters.m_sharedSecret = agreement.CalculateSecret();
+
+ TlsUtilities.Establish13PhaseSecrets(m_tlsClientContext);
+ }
+
+ {
+ InvalidateSession();
+
+ this.m_tlsSession = TlsUtilities.ImportSession(securityParameters.SessionID, null);
+ this.m_sessionParameters = null;
+ this.m_sessionMasterSecret = null;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Process13ServerHelloCoda(ServerHello serverHello, bool afterHelloRetryRequest)
+ {
+ byte[] serverHelloTranscriptHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
+
+ TlsUtilities.Establish13PhaseHandshake(m_tlsClientContext, serverHelloTranscriptHash, m_recordStream);
+
+ // See RFC 8446 D.4.
+ if (!afterHelloRetryRequest)
+ {
+ m_recordStream.SetIgnoreChangeCipherSpec(true);
+
+ // TODO[tls13] If offering early data, the record is placed immediately after the first ClientHello.
+ /*
+ * TODO[tls13] Ideally wait until just after Server Finished received, but then we'd need to defer
+ * the enabling of the pending write cipher
+ */
+ SendChangeCipherSpecMessage();
+ }
+
+ m_recordStream.EnablePendingCipherWrite();
+ m_recordStream.EnablePendingCipherRead(false);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessServerHello(ServerHello serverHello)
+ {
+ IDictionary serverHelloExtensions = serverHello.Extensions;
+
+ ProtocolVersion legacy_version = serverHello.Version;
+ ProtocolVersion supported_version = TlsExtensionsUtilities.GetSupportedVersionsExtensionServer(
+ serverHelloExtensions);
+
+ ProtocolVersion server_version;
+ if (null == supported_version)
+ {
+ server_version = legacy_version;
+ }
+ else
+ {
+ if (!ProtocolVersion.TLSv12.Equals(legacy_version) ||
+ !ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(supported_version))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ server_version = supported_version;
+ }
+
+ SecurityParameters securityParameters = m_tlsClientContext.SecurityParameters;
+
+ // NOT renegotiating
+ {
+ if (!ProtocolVersion.Contains(m_tlsClientContext.ClientSupportedVersions, server_version))
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+
+ ProtocolVersion legacy_record_version = server_version.IsLaterVersionOf(ProtocolVersion.TLSv12)
+ ? ProtocolVersion.TLSv12
+ : server_version;
+
+ m_recordStream.SetWriteVersion(legacy_record_version);
+ securityParameters.m_negotiatedVersion = server_version;
+ }
+
+ TlsUtilities.NegotiatedVersionTlsClient(m_tlsClientContext, m_tlsClient);
+
+ if (ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(server_version))
+ {
+ Process13ServerHello(serverHello, false);
+ return;
+ }
+
+ int[] offeredCipherSuites = m_clientHello.CipherSuites;
+
+ this.m_clientHello = null;
+ this.m_retryCookie = null;
+ this.m_retryGroup = -1;
+
+ securityParameters.m_serverRandom = serverHello.Random;
+
+ if (!m_tlsClientContext.ClientVersion.Equals(server_version))
+ {
+ TlsUtilities.CheckDowngradeMarker(server_version, securityParameters.ServerRandom);
+ }
+
+ {
+ byte[] selectedSessionID = serverHello.SessionID;
+ securityParameters.m_sessionID = selectedSessionID;
+ m_tlsClient.NotifySessionID(selectedSessionID);
+ this.m_resumedSession = selectedSessionID.Length > 0 && m_tlsSession != null
+ && Arrays.AreEqual(selectedSessionID, m_tlsSession.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 cipherSuite = serverHello.CipherSuite;
+
+ if (!TlsUtilities.IsValidCipherSuiteSelection(offeredCipherSuites, cipherSuite) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ m_tlsClient.NotifySelectedCipherSuite(cipherSuite);
+ }
+
+ /*
+ * 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.
+ */
+ this.m_serverExtensions = serverHelloExtensions;
+ if (m_serverExtensions != null)
+ {
+ foreach (int extType in m_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 (ExtensionType.renegotiation_info == extType)
+ 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(m_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 (m_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);
+ }
+ }
+ }
+
+ byte[] renegExtData = TlsUtilities.GetExtensionData(m_serverExtensions, ExtensionType.renegotiation_info);
+
+ // NOT renegotiating
+ {
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake (both full and session-resumption)
+ */
+
+ /*
+ * When a ServerHello is received, the client MUST check if it includes the
+ * "renegotiation_info" extension:
+ */
+ if (renegExtData == null)
+ {
+ /*
+ * 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.
+ */
+ securityParameters.m_secureRenegotiation = false;
+ }
+ else
+ {
+ /*
+ * 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).
+ */
+ securityParameters.m_secureRenegotiation = 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
+ m_tlsClient.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
+
+ /*
+ * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+ * master secret [..]. (and see 5.2, 5.3)
+ *
+ * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
+ * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
+ * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
+ */
+ {
+ bool acceptedExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
+ m_serverExtensions);
+
+ if (acceptedExtendedMasterSecret)
+ {
+ if (server_version.IsSsl
+ || (!m_resumedSession && !m_tlsClient.ShouldUseExtendedMasterSecret()))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ else
+ {
+ if (m_tlsClient.RequiresExtendedMasterSecret()
+ || (m_resumedSession && !m_tlsClient.AllowLegacyResumption()))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ securityParameters.m_extendedMasterSecret = acceptedExtendedMasterSecret;
+ }
+
+ /*
+ * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
+ * contents of this extension are irrelevant, and only the values in the new handshake
+ * messages are considered.
+ */
+ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
+ m_serverExtensions);
+ securityParameters.m_applicationProtocolSet = true;
+
+ IDictionary sessionClientExtensions = m_clientExtensions, sessionServerExtensions = m_serverExtensions;
+ if (m_resumedSession)
+ {
+ if (securityParameters.CipherSuite != m_sessionParameters.CipherSuite
+ || !server_version.Equals(m_sessionParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ sessionClientExtensions = null;
+ sessionServerExtensions = m_sessionParameters.ReadServerExtensions();
+ }
+
+ if (sessionServerExtensions != null && sessionServerExtensions.Count > 0)
+ {
+ {
+ /*
+ * 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.m_encryptThenMac = serverSentEncryptThenMAC;
+ }
+
+ securityParameters.m_maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
+ sessionServerExtensions, AlertDescription.illegal_parameter);
+
+ securityParameters.m_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.
+ */
+ if (!m_resumedSession)
+ {
+ // TODO[tls13] See RFC 8446 4.4.2.1
+ if (TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+ ExtensionType.status_request_v2, AlertDescription.illegal_parameter))
+ {
+ securityParameters.m_statusRequestVersion = 2;
+ }
+ else if (TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+ ExtensionType.status_request, AlertDescription.illegal_parameter))
+ {
+ securityParameters.m_statusRequestVersion = 1;
+ }
+
+ this.m_expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+ ExtensionType.session_ticket, AlertDescription.illegal_parameter);
+ }
+ }
+
+ if (sessionClientExtensions != null)
+ {
+ m_tlsClient.ProcessServerExtensions(sessionServerExtensions);
+ }
+
+ ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
+
+ if (m_resumedSession)
+ {
+ securityParameters.m_masterSecret = m_sessionMasterSecret;
+ m_recordStream.SetPendingCipher(TlsUtilities.InitCipher(m_tlsClientContext));
+ }
+ else
+ {
+ InvalidateSession();
+
+ this.m_tlsSession = TlsUtilities.ImportSession(securityParameters.SessionID, null);
+ this.m_sessionParameters = null;
+ this.m_sessionMasterSecret = null;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13CertificateRequest(MemoryStream buf, bool postHandshakeAuth)
+ {
+ /*
+ * RFC 8446 4.3.2. A server which is authenticating with a certificate MAY optionally
+ * request a certificate from the client.
+ */
+
+ /*
+ * TODO[tls13] Currently all handshakes are certificate-authenticated. When PSK-only becomes an option,
+ * then check here that a certificate message is expected (else fatal unexpected_message alert).
+ */
+
+ CertificateRequest certificateRequest = CertificateRequest.Parse(m_tlsClientContext, buf);
+
+ AssertEmpty(buf);
+
+ if (postHandshakeAuth)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (!certificateRequest.HasCertificateRequestContext(TlsUtilities.EmptyBytes))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.m_certificateRequest = certificateRequest;
+
+ TlsUtilities.EstablishServerSigAlgs(m_tlsClientContext.SecurityParameters, certificateRequest);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13EncryptedExtensions(MemoryStream buf)
+ {
+ byte[] extBytes = TlsUtilities.ReadOpaque16(buf);
+
+ AssertEmpty(buf);
+
+
+ this.m_serverExtensions = ReadExtensionsData13(HandshakeType.encrypted_extensions, extBytes);
+
+ {
+ /*
+ * RFC 8446 4.2. Implementations MUST NOT send extension responses if the remote
+ * endpoint did not send the corresponding extension requests, with the exception of the
+ * "cookie" extension in the HelloRetryRequest. Upon receiving such an extension, an
+ * endpoint MUST abort the handshake with an "unsupported_extension" alert.
+ */
+ foreach (int extType in m_serverExtensions.Keys)
+ {
+ if (null == TlsUtilities.GetExtensionData(m_clientExtensions, extType))
+ throw new TlsFatalAlert(AlertDescription.unsupported_extension);
+ }
+ }
+
+
+ SecurityParameters securityParameters = m_tlsClientContext.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
+ m_serverExtensions);
+ securityParameters.m_applicationProtocolSet = true;
+
+ IDictionary sessionClientExtensions = m_clientExtensions, sessionServerExtensions = m_serverExtensions;
+ if (m_resumedSession)
+ {
+ if (securityParameters.CipherSuite != m_sessionParameters.CipherSuite
+ || !negotiatedVersion.Equals(m_sessionParameters.NegotiatedVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ sessionClientExtensions = null;
+ sessionServerExtensions = m_sessionParameters.ReadServerExtensions();
+ }
+
+ securityParameters.m_maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
+ sessionServerExtensions, AlertDescription.illegal_parameter);
+
+ securityParameters.m_encryptThenMac = false;
+ securityParameters.m_truncatedHmac = false;
+
+ /*
+ * TODO[tls13] RFC 8446 4.4.2.1. OCSP Status and SCT Extensions.
+ *
+ * OCSP information is carried in an extension for a CertificateEntry.
+ */
+ securityParameters.m_statusRequestVersion = m_clientExtensions.Contains(ExtensionType.status_request)
+ ? 1 : 0;
+
+ this.m_expectSessionTicket = false;
+
+ if (null != sessionClientExtensions)
+ {
+ m_tlsClient.ProcessServerExtensions(m_serverExtensions);
+ }
+
+ ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13NewSessionTicket(MemoryStream buf)
+ {
+ if (!IsApplicationDataReady)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ // TODO[tls13] Do something more than just ignore them
+
+ // struct {
+ // uint32 ticket_lifetime;
+ // uint32 ticket_age_add;
+ // opaque ticket_nonce<0..255>;
+ // opaque ticket<1..2^16-1>;
+ // Extension extensions<0..2^16-2>;
+ // } NewSessionTicket;
+
+ TlsUtilities.ReadUint32(buf);
+ TlsUtilities.ReadUint32(buf);
+ TlsUtilities.ReadOpaque8(buf);
+ TlsUtilities.ReadOpaque16(buf);
+ TlsUtilities.ReadOpaque16(buf);
+ AssertEmpty(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13ServerCertificate(MemoryStream buf)
+ {
+ this.m_authentication = TlsUtilities.Receive13ServerCertificate(m_tlsClientContext, m_tlsClient, buf);
+
+ // NOTE: In TLS 1.3 we don't have to wait for a possible CertificateStatus message.
+ HandleServerCertificate();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13ServerCertificateVerify(MemoryStream buf)
+ {
+ Certificate serverCertificate = m_tlsClientContext.SecurityParameters.PeerCertificate;
+ if (null == serverCertificate || serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ // TODO[tls13] Actual structure is 'CertificateVerify' in RFC 8446, consider adding for clarity
+ DigitallySigned certificateVerify = DigitallySigned.Parse(m_tlsClientContext, buf);
+
+ AssertEmpty(buf);
+
+ TlsUtilities.Verify13CertificateVerifyServer(m_tlsClientContext, certificateVerify, m_handshakeHash);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13ServerFinished(MemoryStream buf)
+ {
+ Process13FinishedMessage(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ReceiveCertificateRequest(MemoryStream buf)
+ {
+ if (null == m_authentication)
+ {
+ /*
+ * 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);
+ }
+
+ CertificateRequest certificateRequest = CertificateRequest.Parse(m_tlsClientContext, buf);
+
+ AssertEmpty(buf);
+
+ this.m_certificateRequest = TlsUtilities.ValidateCertificateRequest(certificateRequest, m_keyExchange);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ReceiveNewSessionTicket(MemoryStream buf)
+ {
+ NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf);
+
+ AssertEmpty(buf);
+
+ m_tlsClient.NotifyNewSessionTicket(newSessionTicket);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual ServerHello ReceiveServerHelloMessage(MemoryStream buf)
+ {
+ return ServerHello.Parse(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13ClientHelloRetry()
+ {
+ IDictionary clientHelloExtensions = m_clientHello.Extensions;
+
+ clientHelloExtensions.Remove(ExtensionType.cookie);
+ clientHelloExtensions.Remove(ExtensionType.early_data);
+ clientHelloExtensions.Remove(ExtensionType.key_share);
+
+ /*
+ * RFC 4.2.2. When sending the new ClientHello, the client MUST copy the contents of the
+ * extension received in the HelloRetryRequest into a "cookie" extension in the new
+ * ClientHello.
+ */
+ if (null != m_retryCookie)
+ {
+ TlsExtensionsUtilities.AddCookieExtension(clientHelloExtensions, m_retryCookie);
+ this.m_retryCookie = null;
+ }
+
+ /*
+ * RFC 8446 4.2.8. [..] when sending the new ClientHello, the client MUST replace the
+ * original "key_share" extension with one containing only a new KeyShareEntry for the group
+ * indicated in the selected_group field of the triggering HelloRetryRequest.
+ */
+ if (m_retryGroup < 0)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_clientAgreements = TlsUtilities.AddKeyShareToClientHelloRetry(m_tlsClientContext,
+ clientHelloExtensions, m_retryGroup);
+
+ /*
+ * TODO[tls13] Updating the "pre_shared_key" extension if present by recomputing the
+ * "obfuscated_ticket_age" and binder values and (optionally) removing any PSKs which are
+ * incompatible with the server's indicated cipher suite.
+ */
+
+ /*
+ * TODO[tls13] Optionally adding, removing, or changing the length of the "padding"
+ * extension [RFC7685].
+ */
+
+ // See RFC 8446 D.4.
+ {
+ m_recordStream.SetIgnoreChangeCipherSpec(true);
+
+ // TODO[tls13] If offering early data, the record is placed immediately after the first ClientHello.
+ SendChangeCipherSpecMessage();
+ }
+
+ SendClientHelloMessage();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_verify);
+ certificateVerify.Encode(message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendClientHello()
+ {
+ SecurityParameters securityParameters = m_tlsClientContext.SecurityParameters;
+
+ ProtocolVersion client_version;
+
+ // NOT renegotiating
+ {
+ m_tlsClientContext.SetClientSupportedVersions(m_tlsClient.GetProtocolVersions());
+
+ if (ProtocolVersion.Contains(m_tlsClientContext.ClientSupportedVersions, ProtocolVersion.SSLv3))
+ {
+ // TODO[tls13] Prevent offering SSLv3 AND TLSv13?
+ m_recordStream.SetWriteVersion(ProtocolVersion.SSLv3);
+ }
+ else
+ {
+ m_recordStream.SetWriteVersion(ProtocolVersion.TLSv10);
+ }
+
+ client_version = ProtocolVersion.GetLatestTls(m_tlsClientContext.ClientSupportedVersions);
+
+ if (!ProtocolVersion.IsSupportedTlsVersionClient(client_version))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ m_tlsClientContext.SetClientVersion(client_version);
+ }
+
+ bool offeringTlsV13Plus = ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(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[] legacy_session_id = TlsUtilities.GetSessionID(m_tlsSession);
+
+ bool fallback = m_tlsClient.IsFallback();
+
+ int[] offeredCipherSuites = m_tlsClient.GetCipherSuites();
+
+ if (legacy_session_id.Length > 0 && m_sessionParameters != null)
+ {
+ if (!Arrays.Contains(offeredCipherSuites, m_sessionParameters.CipherSuite))
+ {
+ legacy_session_id = TlsUtilities.EmptyBytes;
+ }
+ }
+
+ this.m_clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ m_tlsClient.GetClientExtensions());
+
+ ProtocolVersion legacy_version = client_version;
+ if (offeringTlsV13Plus)
+ {
+ legacy_version = ProtocolVersion.TLSv12;
+
+ TlsExtensionsUtilities.AddSupportedVersionsExtensionClient(m_clientExtensions,
+ m_tlsClientContext.ClientSupportedVersions);
+
+ /*
+ * RFC 8446 4.2.1. In compatibility mode [..], this field MUST be non-empty, so a client
+ * not offering a pre-TLS 1.3 session MUST generate a new 32-byte value.
+ */
+ if (legacy_session_id.Length < 1)
+ {
+ legacy_session_id = m_tlsClientContext.NonceGenerator.GenerateNonce(32);
+ }
+ }
+
+ m_tlsClientContext.SetRsaPreMasterSecretVersion(legacy_version);
+
+ securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
+ m_clientExtensions);
+
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(client_version))
+ {
+ TlsUtilities.EstablishClientSigAlgs(securityParameters, m_clientExtensions);
+ }
+
+ securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
+ m_clientExtensions);
+
+ this.m_clientAgreements = TlsUtilities.AddEarlyKeySharesToClientHello(m_tlsClientContext, m_tlsClient,
+ m_clientExtensions);
+
+ if (TlsUtilities.IsExtendedMasterSecretOptionalTls(m_tlsClientContext.ClientSupportedVersions)
+ && (m_tlsClient.ShouldUseExtendedMasterSecret() ||
+ (null != m_sessionParameters && m_sessionParameters.IsExtendedMasterSecret)))
+ {
+ TlsExtensionsUtilities.AddExtendedMasterSecretExtension(m_clientExtensions);
+ }
+ else if (!offeringTlsV13Plus && m_tlsClient.RequiresExtendedMasterSecret())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ {
+ bool useGmtUnixTime = !offeringTlsV13Plus && m_tlsClient.ShouldUseGmtUnixTime();
+
+ securityParameters.m_clientRandom = CreateRandomBlock(useGmtUnixTime, m_tlsClientContext);
+ }
+
+ // NOT renegotiating
+ {
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake (both full and session-resumption)
+ */
+
+ /*
+ * 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 = (null == TlsUtilities.GetExtensionData(m_clientExtensions,
+ ExtensionType.renegotiation_info));
+ bool noRenegScsv = !Arrays.Contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+
+ if (noRenegExt && noRenegScsv)
+ {
+ // TODO[tls13] Probably want to not add this if no pre-TLSv13 versions offered?
+ offeredCipherSuites = Arrays.Append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ }
+ }
+
+ /*
+ * (Fallback SCSV)
+ * RFC 7507 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 [..]. (The
+ * client SHOULD put TLS_FALLBACK_SCSV after all cipher suites that it actually intends
+ * to negotiate.)
+ */
+ if (fallback && !Arrays.Contains(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
+ {
+ offeredCipherSuites = Arrays.Append(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
+ }
+
+
+
+ this.m_clientHello = new ClientHello(legacy_version, securityParameters.ClientRandom, legacy_session_id,
+ null, offeredCipherSuites, m_clientExtensions);
+
+ SendClientHelloMessage();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendClientHelloMessage()
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.client_hello);
+ m_clientHello.Encode(m_tlsClientContext, message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendClientKeyExchange()
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.client_key_exchange);
+ m_keyExchange.GenerateClientKeyExchange(message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Skip13CertificateRequest()
+ {
+ this.m_certificateRequest = null;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Skip13ServerCertificate()
+ {
+ this.m_authentication = null;
+
+ // TODO[tls13] May be skipped for PSK handshakes?
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsCloseable.cs b/crypto/src/tls/TlsCloseable.cs
new file mode 100644
index 000000000..baf2ff444
--- /dev/null
+++ b/crypto/src/tls/TlsCloseable.cs
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public interface TlsCloseable
+ {
+ /// <exception cref="IOException"/>
+ void Close();
+ }
+}
diff --git a/crypto/src/tls/TlsContext.cs b/crypto/src/tls/TlsContext.cs
new file mode 100644
index 000000000..5a2802f56
--- /dev/null
+++ b/crypto/src/tls/TlsContext.cs
@@ -0,0 +1,79 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for a TLS context implementation.</summary>
+ public interface TlsContext
+ {
+ TlsCrypto Crypto { get; }
+
+ TlsNonceGenerator NonceGenerator { get; }
+
+ SecurityParameters SecurityParameters { get; }
+
+ /// <summary>Return true if this context is for a server, false otherwise.</summary>
+ /// <returns>true for a server based context, false for a client based one.</returns>
+ bool IsServer { get; }
+
+ ProtocolVersion[] ClientSupportedVersions { get; }
+
+ ProtocolVersion ClientVersion { get; }
+
+ ProtocolVersion RsaPreMasterSecretVersion { get; }
+
+ ProtocolVersion ServerVersion { get; }
+
+ /// <summary>Used to get the resumable session, if any, used by this connection.</summary>
+ /// <remarks>
+ /// Only available after the handshake has successfully completed.
+ /// </remarks>
+ /// <returns>A <see cref="TlsSession"/> representing the resumable session used by this connection, or null if
+ /// no resumable session available.</returns>
+ /// <seealso cref="TlsPeer.NotifyHandshakeComplete"/>
+ TlsSession ResumableSession { get; }
+
+ /// <summary>Used to get the session information for this connection.</summary>
+ /// <remarks>
+ /// Only available after the handshake has successfully completed. Use <see cref="TlsSession.IsResumable"/>
+ /// to find out if the session is resumable.
+ /// </remarks>
+ /// <returns>A <see cref="TlsSession"/> representing the session used by this connection.</returns>
+ /// <seealso cref="TlsPeer.NotifyHandshakeComplete"/>
+ TlsSession Session { get; }
+
+ object UserObject { get; set; }
+
+ /// <summary>Export the value of the specified channel binding.</summary>
+ /// <remarks>
+ /// Only available after the handshake has successfully completed.
+ /// </remarks>
+ /// <param name="channelBinding">A <see cref="ChannelBinding"/> constant specifying the channel binding to
+ /// export.</param>
+ /// <returns>A copy of the channel binding data as a <c>byte[]</c>, or null if the binding could not be
+ /// determined.</returns>
+ byte[] ExportChannelBinding(int channelBinding);
+
+ /// <summary>Export (early data) keying material according to RFC 5705: "Keying Material Exporters for TLS", as
+ /// updated for TLS 1.3 (RFC 8446).</summary>
+ /// <remarks>
+ /// NOTE: for use in settings where an exporter is needed for 0-RTT data.
+ /// </remarks>
+ /// <param name="asciiLabel">indicates which application will use the exported keys.</param>
+ /// <param name="context_value">allows the application using the exporter to mix its own data with the TLS PRF
+ /// for the exporter output.</param>
+ /// <param name="length">the number of bytes to generate.</param>
+ /// <returns>a pseudorandom bit string of 'length' bytes generated from the (exporter_)master_secret.</returns>
+ byte[] ExportEarlyKeyingMaterial(string asciiLabel, byte[] context_value, int length);
+
+ /// <summary>Export keying material according to RFC 5705: "Keying Material Exporters for TLS", as updated for
+ /// TLS 1.3 (RFC 8446) when negotiated.</summary>
+ /// <param name="asciiLabel">indicates which application will use the exported keys.</param>
+ /// <param name="context_value">allows the application using the exporter to mix its own data with the TLS PRF
+ /// for the exporter output.</param>
+ /// <param name="length">the number of bytes to generate.</param>
+ /// <returns>a pseudorandom bit string of 'length' bytes generated from the (exporter_)master_secret.</returns>
+ byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length);
+ }
+}
diff --git a/crypto/src/tls/TlsCredentialedAgreement.cs b/crypto/src/tls/TlsCredentialedAgreement.cs
new file mode 100644
index 000000000..354a17754
--- /dev/null
+++ b/crypto/src/tls/TlsCredentialedAgreement.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Support interface for generating a secret based on the credentials sent by a TLS peer.</summary>
+ public interface TlsCredentialedAgreement
+ : TlsCredentials
+ {
+ /// <summary>Calculate an agreed secret based on our credentials and the public key credentials of our peer.
+ /// </summary>
+ /// <param name="peerCertificate">public key certificate of our TLS peer.</param>
+ /// <returns>the agreed secret.</returns>
+ /// <exception cref="IOException">in case of an exception on generation of the secret.</exception>
+ TlsSecret GenerateAgreement(TlsCertificate peerCertificate);
+ }
+}
diff --git a/crypto/src/tls/TlsCredentialedDecryptor.cs b/crypto/src/tls/TlsCredentialedDecryptor.cs
new file mode 100644
index 000000000..5fa021d41
--- /dev/null
+++ b/crypto/src/tls/TlsCredentialedDecryptor.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for a class that decrypts TLS secrets.</summary>
+ public interface TlsCredentialedDecryptor
+ : TlsCredentials
+ {
+ /// <summary>Decrypt the passed in cipher text using the parameters available.</summary>
+ /// <param name="cryptoParams">the parameters to use for the decryption.</param>
+ /// <param name="ciphertext">the cipher text containing the secret.</param>
+ /// <returns>a TLS secret.</returns>
+ /// <exception cref="IOException">on a parsing or decryption error.</exception>
+ TlsSecret Decrypt(TlsCryptoParameters cryptoParams, byte[] ciphertext);
+ }
+}
diff --git a/crypto/src/tls/TlsCredentialedSigner.cs b/crypto/src/tls/TlsCredentialedSigner.cs
new file mode 100644
index 000000000..c6f5a8d7f
--- /dev/null
+++ b/crypto/src/tls/TlsCredentialedSigner.cs
@@ -0,0 +1,26 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Support interface for generating a signature based on our private credentials.</summary>
+ public interface TlsCredentialedSigner
+ : TlsCredentials
+ {
+ /// <summary>Generate a signature against the passed in hash.</summary>
+ /// <param name="hash">a message digest calculated across the message the signature is to apply to.</param>
+ /// <returns>an encoded signature.</returns>
+ /// <exception cref="IOException">if the hash cannot be processed, or there is an issue with the private
+ /// credentials.</exception>
+ byte[] GenerateRawSignature(byte[] hash);
+
+ /// <summary>Return the algorithm IDs for the signature algorithm and the associated hash it uses.</summary>
+ /// <returns>the full algorithm details for the signature.</returns>
+ SignatureAndHashAlgorithm SignatureAndHashAlgorithm { get; }
+
+ /// <exception cref="IOException"/>
+ TlsStreamSigner GetStreamSigner();
+ }
+}
diff --git a/crypto/src/tls/TlsCredentials.cs b/crypto/src/tls/TlsCredentials.cs
new file mode 100644
index 000000000..4a6084a07
--- /dev/null
+++ b/crypto/src/tls/TlsCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for interfaces/classes carrying TLS credentials.</summary>
+ public interface TlsCredentials
+ {
+ /// <summary>Return the certificate structure representing our identity.</summary>
+ /// <returns>our certificate structure.</returns>
+ Certificate Certificate { get; }
+ }
+}
diff --git a/crypto/src/tls/TlsDHGroupVerifier.cs b/crypto/src/tls/TlsDHGroupVerifier.cs
new file mode 100644
index 000000000..2fb208cc0
--- /dev/null
+++ b/crypto/src/tls/TlsDHGroupVerifier.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Interface for verifying explicit Diffie-Hellman group parameters.</summary>
+ public interface TlsDHGroupVerifier
+ {
+ /// <summary>Check whether the given DH group is acceptable for use.</summary>
+ /// <param name="dhGroup">the <see cref="DHGroup"/> to check.</param>
+ /// <returns>true if (and only if) the specified group is acceptable.</returns>
+ bool Accept(DHGroup dhGroup);
+ }
+}
diff --git a/crypto/src/tls/TlsDHKeyExchange.cs b/crypto/src/tls/TlsDHKeyExchange.cs
new file mode 100644
index 000000000..a00ed67b7
--- /dev/null
+++ b/crypto/src/tls/TlsDHKeyExchange.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS DH key exchange.</summary>
+ public class TlsDHKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DH_RSA:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsCredentialedAgreement m_agreementCredentials;
+ protected TlsCertificate m_dhPeerCertificate;
+
+ public TlsDHKeyExchange(int keyExchange)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ }
+
+ public override void SkipServerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ this.m_agreementCredentials = TlsUtilities.RequireAgreementCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ this.m_dhPeerCertificate = serverCertificate.GetCertificateAt(0).CheckUsageInRole(ConnectionEnd.server,
+ TlsCertificateRole.DH);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ return new short[]{ ClientCertificateType.dss_fixed_dh, ClientCertificateType.rsa_fixed_dh };
+ }
+
+ public override void SkipClientCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ this.m_agreementCredentials = TlsUtilities.RequireAgreementCredentials(clientCredentials);
+ }
+
+ 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.
+ */
+ }
+
+ public override void ProcessClientCertificate(Certificate clientCertificate)
+ {
+ this.m_dhPeerCertificate = clientCertificate.GetCertificateAt(0).CheckUsageInRole(ConnectionEnd.client,
+ TlsCertificateRole.DH);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ // For dss_fixed_dh and rsa_fixed_dh, the key arrived in the client certificate
+ }
+
+ public override bool RequiresCertificateVerify
+ {
+ get { return false; }
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ return m_agreementCredentials.GenerateAgreement(m_dhPeerCertificate);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsDHUtilities.cs b/crypto/src/tls/TlsDHUtilities.cs
new file mode 100644
index 000000000..7605b0055
--- /dev/null
+++ b/crypto/src/tls/TlsDHUtilities.cs
@@ -0,0 +1,159 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class TlsDHUtilities
+ {
+ public static TlsDHConfig CreateNamedDHConfig(TlsContext context, int namedGroup)
+ {
+ if (namedGroup < 0 || NamedGroup.GetFiniteFieldBits(namedGroup) < 1)
+ return null;
+
+ bool padded = TlsUtilities.IsTlsV13(context);
+ return new TlsDHConfig(namedGroup, padded);
+ }
+
+ public static DHGroup GetDHGroup(TlsDHConfig dhConfig)
+ {
+ int namedGroup = dhConfig.NamedGroup;
+ if (namedGroup >= 0)
+ return GetNamedDHGroup(namedGroup);
+
+ return dhConfig.ExplicitGroup;
+ }
+
+ public static DHGroup GetNamedDHGroup(int namedGroup)
+ {
+ switch (namedGroup)
+ {
+ case NamedGroup.ffdhe2048:
+ return DHStandardGroups.rfc7919_ffdhe2048;
+ case NamedGroup.ffdhe3072:
+ return DHStandardGroups.rfc7919_ffdhe3072;
+ case NamedGroup.ffdhe4096:
+ return DHStandardGroups.rfc7919_ffdhe4096;
+ case NamedGroup.ffdhe6144:
+ return DHStandardGroups.rfc7919_ffdhe6144;
+ case NamedGroup.ffdhe8192:
+ return DHStandardGroups.rfc7919_ffdhe8192;
+ default:
+ return null;
+ }
+ }
+
+ public static int GetMinimumFiniteFieldBits(int cipherSuite)
+ {
+ /*
+ * NOTE: An equivalent mechanism was added to support a minimum bit-size requirement for ECC
+ * mooted in early drafts of RFC 8442. This requirement was removed in later drafts, so that
+ * mechanism is currently somewhat trivial, and this similarly so.
+ */
+ return IsDHCipherSuite(cipherSuite) ? 1 : 0;
+ }
+
+ public static bool IsDHCipherSuite(int cipherSuite)
+ {
+ switch (TlsUtilities.GetKeyExchangeAlgorithm(cipherSuite))
+ {
+ case KeyExchangeAlgorithm.DH_anon:
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public static int GetNamedGroupForDHParameters(BigInteger p, BigInteger g)
+ {
+ int[] namedGroups = new int[]{ NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096,
+ NamedGroup.ffdhe6144, NamedGroup.ffdhe8192 };
+
+ for (int i = 0; i < namedGroups.Length; ++i)
+ {
+ int namedGroup = namedGroups[i];
+ DHGroup dhGroup = GetNamedDHGroup(namedGroup);
+ if (dhGroup != null && dhGroup.P.Equals(p) && dhGroup.G.Equals(g))
+ return namedGroup;
+ }
+
+ return -1;
+ }
+
+ public static DHGroup GetStandardGroupForDHParameters(BigInteger p, BigInteger g)
+ {
+ DHGroup[] standardGroups = new DHGroup[] { DHStandardGroups.rfc7919_ffdhe2048,
+ DHStandardGroups.rfc7919_ffdhe3072, DHStandardGroups.rfc7919_ffdhe4096, DHStandardGroups.rfc7919_ffdhe6144,
+ DHStandardGroups.rfc7919_ffdhe8192, DHStandardGroups.rfc3526_1536, DHStandardGroups.rfc3526_2048,
+ DHStandardGroups.rfc3526_3072, DHStandardGroups.rfc3526_4096, DHStandardGroups.rfc3526_6144,
+ DHStandardGroups.rfc3526_8192, DHStandardGroups.rfc5996_768, DHStandardGroups.rfc5996_1024 };
+
+ for (int i = 0; i < standardGroups.Length; ++i)
+ {
+ DHGroup dhGroup = standardGroups[i];
+ if (dhGroup != null && dhGroup.P.Equals(p) && dhGroup.G.Equals(g))
+ return dhGroup;
+ }
+
+ return null;
+ }
+
+ /// <exception cref="IOException"/>
+ public static TlsDHConfig ReceiveDHConfig(TlsContext context, TlsDHGroupVerifier dhGroupVerifier,
+ Stream input)
+ {
+ BigInteger p = ReadDHParameter(input);
+ BigInteger g = ReadDHParameter(input);
+
+ int namedGroup = GetNamedGroupForDHParameters(p, g);
+ if (namedGroup< 0)
+ {
+ DHGroup dhGroup = GetStandardGroupForDHParameters(p, g);
+ if (null == dhGroup)
+ {
+ dhGroup = new DHGroup(p, null, g, 0);
+ }
+
+ if (!dhGroupVerifier.Accept(dhGroup))
+ throw new TlsFatalAlert(AlertDescription.insufficient_security);
+
+ return new TlsDHConfig(dhGroup);
+ }
+
+ int[] clientSupportedGroups = context.SecurityParameters.ClientSupportedGroups;
+ if (null == clientSupportedGroups || Arrays.Contains(clientSupportedGroups, namedGroup))
+ return new TlsDHConfig(namedGroup, false);
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /// <exception cref="IOException"/>
+ public static BigInteger ReadDHParameter(Stream input)
+ {
+ return new BigInteger(1, TlsUtilities.ReadOpaque16(input, 1));
+ }
+
+ /// <exception cref="IOException"/>
+ public static void WriteDHConfig(TlsDHConfig dhConfig, Stream output)
+ {
+ DHGroup group = GetDHGroup(dhConfig);
+ WriteDHParameter(group.P, output);
+ WriteDHParameter(group.G, output);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void WriteDHParameter(BigInteger x, Stream output)
+ {
+ TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsDHanonKeyExchange.cs b/crypto/src/tls/TlsDHanonKeyExchange.cs
new file mode 100644
index 000000000..dd03ce0f1
--- /dev/null
+++ b/crypto/src/tls/TlsDHanonKeyExchange.cs
@@ -0,0 +1,124 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS DH_anon key exchange.</summary>
+ public class TlsDHanonKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_anon:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsDHGroupVerifier m_dhGroupVerifier;
+ protected TlsDHConfig m_dhConfig;
+
+ protected TlsAgreement m_agreement;
+
+ public TlsDHanonKeyExchange(int keyExchange, TlsDHGroupVerifier dhGroupVerifier)
+ : this(keyExchange, dhGroupVerifier, null)
+ {
+ }
+
+ public TlsDHanonKeyExchange(int keyExchange, TlsDHConfig dhConfig)
+ : this(keyExchange, null, dhConfig)
+ {
+ }
+
+ private TlsDHanonKeyExchange(int keyExchange, TlsDHGroupVerifier dhGroupVerifier, TlsDHConfig dhConfig)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_dhGroupVerifier = dhGroupVerifier;
+ this.m_dhConfig = dhConfig;
+ }
+
+ public override void SkipServerCredentials()
+ {
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override bool RequiresServerKeyExchange
+ {
+ get { return true; }
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ MemoryStream buf = new MemoryStream();
+
+ TlsDHUtilities.WriteDHConfig(m_dhConfig, buf);
+
+ this.m_agreement = m_context.Crypto.CreateDHDomain(m_dhConfig).CreateDH();
+
+ byte[] y = m_agreement.GenerateEphemeral();
+
+ TlsUtilities.WriteOpaque16(y, buf);
+
+ return buf. ToArray();
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ this.m_dhConfig = TlsDHUtilities.ReceiveDHConfig(m_context, m_dhGroupVerifier, input);
+
+ byte[] y = TlsUtilities.ReadOpaque16(input, 1);
+
+ this.m_agreement = m_context.Crypto.CreateDHDomain(m_dhConfig).CreateDH();
+
+ m_agreement.ReceivePeerValue(y);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ return null;
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ byte[] y = m_agreement.GenerateEphemeral();
+
+ TlsUtilities.WriteOpaque16(y, output);
+ }
+
+ public override void ProcessClientCertificate(Certificate clientCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ byte[] y = TlsUtilities.ReadOpaque16(input, 1);
+
+ m_agreement.ReceivePeerValue(y);
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ return m_agreement.CalculateSecret();
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsDheKeyExchange.cs b/crypto/src/tls/TlsDheKeyExchange.cs
new file mode 100644
index 000000000..dd41b1260
--- /dev/null
+++ b/crypto/src/tls/TlsDheKeyExchange.cs
@@ -0,0 +1,129 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsDheKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsDHGroupVerifier m_dhGroupVerifier;
+ protected TlsDHConfig m_dhConfig;
+
+ protected TlsCredentialedSigner m_serverCredentials = null;
+ protected TlsCertificate m_serverCertificate = null;
+ protected TlsAgreement m_agreement;
+
+ public TlsDheKeyExchange(int keyExchange, TlsDHGroupVerifier dhGroupVerifier)
+ : this(keyExchange, dhGroupVerifier, null)
+ {
+ }
+
+ public TlsDheKeyExchange(int keyExchange, TlsDHConfig dhConfig)
+ : this(keyExchange, null, dhConfig)
+ {
+ }
+
+ private TlsDheKeyExchange(int keyExchange, TlsDHGroupVerifier dhGroupVerifier, TlsDHConfig dhConfig)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_dhGroupVerifier = dhGroupVerifier;
+ this.m_dhConfig = dhConfig;
+ }
+
+ public override void SkipServerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ this.m_serverCredentials = TlsUtilities.RequireSignerCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ this.m_serverCertificate = serverCertificate.GetCertificateAt(0);
+ }
+
+ public override bool RequiresServerKeyExchange
+ {
+ get { return true; }
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ DigestInputBuffer digestBuffer = new DigestInputBuffer();
+
+ TlsDHUtilities.WriteDHConfig(m_dhConfig, digestBuffer);
+
+ this.m_agreement = m_context.Crypto.CreateDHDomain(m_dhConfig).CreateDH();
+
+ byte[] y = m_agreement.GenerateEphemeral();
+
+ TlsUtilities.WriteOpaque16(y, digestBuffer);
+
+ TlsUtilities.GenerateServerKeyExchangeSignature(m_context, m_serverCredentials, digestBuffer);
+
+ return digestBuffer.ToArray();
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ DigestInputBuffer digestBuffer = new DigestInputBuffer();
+ Stream teeIn = new TeeInputStream(input, digestBuffer);
+
+ this.m_dhConfig = TlsDHUtilities.ReceiveDHConfig(m_context, m_dhGroupVerifier, teeIn);
+
+ byte[] y = TlsUtilities.ReadOpaque16(teeIn, 1);
+
+ TlsUtilities.VerifyServerKeyExchangeSignature(m_context, input, m_serverCertificate, digestBuffer);
+
+ this.m_agreement = m_context.Crypto.CreateDHDomain(m_dhConfig).CreateDH();
+
+ m_agreement.ReceivePeerValue(y);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ return new short[]{ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign,
+ ClientCertificateType.rsa_sign };
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ TlsUtilities.RequireSignerCredentials(clientCredentials);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ byte[] y = m_agreement.GenerateEphemeral();
+
+ TlsUtilities.WriteOpaque16(y, output);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ m_agreement.ReceivePeerValue(TlsUtilities.ReadOpaque16(input, 1));
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ return m_agreement.CalculateSecret();
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsECDHKeyExchange.cs b/crypto/src/tls/TlsECDHKeyExchange.cs
new file mode 100644
index 000000000..45e83f8ad
--- /dev/null
+++ b/crypto/src/tls/TlsECDHKeyExchange.cs
@@ -0,0 +1,95 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS ECDH key exchange (see RFC 4492).</summary>
+ public class TlsECDHKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsCredentialedAgreement m_agreementCredentials;
+ protected TlsCertificate m_ecdhPeerCertificate;
+
+ public TlsECDHKeyExchange(int keyExchange)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ }
+
+ public override void SkipServerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ this.m_agreementCredentials = TlsUtilities.RequireAgreementCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ this.m_ecdhPeerCertificate = serverCertificate.GetCertificateAt(0).CheckUsageInRole(ConnectionEnd.server,
+ TlsCertificateRole.ECDH);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ /*
+ * 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.
+ */
+ return new short[]{ ClientCertificateType.ecdsa_fixed_ecdh, ClientCertificateType.rsa_fixed_ecdh };
+ }
+
+ public override void SkipClientCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ this.m_agreementCredentials = TlsUtilities.RequireAgreementCredentials(clientCredentials);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ // In this case, the Client Key Exchange message will be sent, but will be empty.
+ }
+
+ public override void ProcessClientCertificate(Certificate clientCertificate)
+ {
+ this.m_ecdhPeerCertificate = clientCertificate.GetCertificateAt(0).CheckUsageInRole(ConnectionEnd.client,
+ TlsCertificateRole.ECDH);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate
+ }
+
+ public override bool RequiresCertificateVerify
+ {
+ get { return false; }
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ return m_agreementCredentials.GenerateAgreement(m_ecdhPeerCertificate);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsECDHanonKeyExchange.cs b/crypto/src/tls/TlsECDHanonKeyExchange.cs
new file mode 100644
index 000000000..66b0ec9fb
--- /dev/null
+++ b/crypto/src/tls/TlsECDHanonKeyExchange.cs
@@ -0,0 +1,127 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS ECDH_anon key exchange (see RFC 4492).</summary>
+ public class TlsECDHanonKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDH_anon:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsECConfig m_ecConfig;
+
+ protected TlsAgreement m_agreement;
+
+ public TlsECDHanonKeyExchange(int keyExchange)
+ : this(keyExchange, null)
+ {
+ }
+
+ public TlsECDHanonKeyExchange(int keyExchange, TlsECConfig ecConfig)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_ecConfig = ecConfig;
+ }
+
+ public override void SkipServerCredentials()
+ {
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override bool RequiresServerKeyExchange
+ {
+ get { return true; }
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ MemoryStream buf = new MemoryStream();
+
+ TlsEccUtilities.WriteECConfig(m_ecConfig, buf);
+
+ this.m_agreement = m_context.Crypto.CreateECDomain(m_ecConfig).CreateECDH();
+
+ GenerateEphemeral(buf);
+
+ return buf.ToArray();
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ this.m_ecConfig = TlsEccUtilities.ReceiveECDHConfig(m_context, input);
+
+ byte[] point = TlsUtilities.ReadOpaque8(input, 1);
+
+ this.m_agreement = m_context.Crypto.CreateECDomain(m_ecConfig).CreateECDH();
+
+ ProcessEphemeral(point);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ return null;
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ GenerateEphemeral(output);
+ }
+
+ public override void ProcessClientCertificate(Certificate clientCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ byte[] point = TlsUtilities.ReadOpaque8(input, 1);
+
+ ProcessEphemeral(point);
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ return m_agreement.CalculateSecret();
+ }
+
+ protected virtual void GenerateEphemeral(Stream output)
+ {
+ byte[] point = m_agreement.GenerateEphemeral();
+
+ TlsUtilities.WriteOpaque8(point, output);
+ }
+
+ protected virtual void ProcessEphemeral(byte[] point)
+ {
+ TlsEccUtilities.CheckPointEncoding(m_ecConfig.NamedGroup, point);
+
+ this.m_agreement.ReceivePeerValue(point);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsECDheKeyExchange.cs b/crypto/src/tls/TlsECDheKeyExchange.cs
new file mode 100644
index 000000000..ab83036d9
--- /dev/null
+++ b/crypto/src/tls/TlsECDheKeyExchange.cs
@@ -0,0 +1,141 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS ECDHE key exchange (see RFC 4492).</summary>
+ public class TlsECDheKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsECConfig m_ecConfig;
+
+ protected TlsCredentialedSigner m_serverCredentials = null;
+ protected TlsCertificate m_serverCertificate = null;
+ protected TlsAgreement m_agreement;
+
+ public TlsECDheKeyExchange(int keyExchange)
+ : this(keyExchange, null)
+ {
+ }
+
+ public TlsECDheKeyExchange(int keyExchange, TlsECConfig ecConfig)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_ecConfig = ecConfig;
+ }
+
+ public override void SkipServerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ this.m_serverCredentials = TlsUtilities.RequireSignerCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ this.m_serverCertificate = serverCertificate.GetCertificateAt(0);
+ }
+
+ public override bool RequiresServerKeyExchange
+ {
+ get { return true; }
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ DigestInputBuffer digestBuffer = new DigestInputBuffer();
+
+ TlsEccUtilities.WriteECConfig(m_ecConfig, digestBuffer);
+
+ this.m_agreement = m_context.Crypto.CreateECDomain(m_ecConfig).CreateECDH();
+
+ GenerateEphemeral(digestBuffer);
+
+ TlsUtilities.GenerateServerKeyExchangeSignature(m_context, m_serverCredentials, digestBuffer);
+
+ return digestBuffer.ToArray();
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ DigestInputBuffer digestBuffer = new DigestInputBuffer();
+ Stream teeIn = new TeeInputStream(input, digestBuffer);
+
+ this.m_ecConfig = TlsEccUtilities.ReceiveECDHConfig(m_context, teeIn);
+
+ byte[] point = TlsUtilities.ReadOpaque8(teeIn, 1);
+
+ TlsUtilities.VerifyServerKeyExchangeSignature(m_context, input, m_serverCertificate, digestBuffer);
+
+ this.m_agreement = m_context.Crypto.CreateECDomain(m_ecConfig).CreateECDH();
+
+ ProcessEphemeral(point);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ /*
+ * 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.
+ */
+ return new short[]{ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign,
+ ClientCertificateType.rsa_sign };
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ TlsUtilities.RequireSignerCredentials(clientCredentials);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ GenerateEphemeral(output);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ byte[] point = TlsUtilities.ReadOpaque8(input, 1);
+
+ ProcessEphemeral(point);
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ return m_agreement.CalculateSecret();
+ }
+
+ protected virtual void GenerateEphemeral(Stream output)
+ {
+ byte[] point = m_agreement.GenerateEphemeral();
+
+ TlsUtilities.WriteOpaque8(point, output);
+ }
+
+ protected virtual void ProcessEphemeral(byte[] point)
+ {
+ TlsEccUtilities.CheckPointEncoding(m_ecConfig.NamedGroup, point);
+
+ this.m_agreement.ReceivePeerValue(point);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsEccUtilities.cs b/crypto/src/tls/TlsEccUtilities.cs
new file mode 100644
index 000000000..59320a717
--- /dev/null
+++ b/crypto/src/tls/TlsEccUtilities.cs
@@ -0,0 +1,117 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class TlsEccUtilities
+ {
+ /// <exception cref="IOException"/>
+ public static TlsECConfig CreateNamedECConfig(TlsContext context, int namedGroup)
+ {
+ if (NamedGroup.GetCurveBits(namedGroup) < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return new TlsECConfig(namedGroup);
+ }
+
+ public static int GetMinimumCurveBits(int cipherSuite)
+ {
+ /*
+ * NOTE: This mechanism was added to support a minimum bit-size requirement mooted in early
+ * drafts of RFC 8442. This requirement was removed in later drafts, so this mechanism is
+ * currently somewhat trivial.
+ */
+ return IsEccCipherSuite(cipherSuite) ? 1 : 0;
+ }
+
+ public static bool IsEccCipherSuite(int cipherSuite)
+ {
+ switch (TlsUtilities.GetKeyExchangeAlgorithm(cipherSuite))
+ {
+ case KeyExchangeAlgorithm.ECDH_anon:
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public static void CheckPointEncoding(int namedGroup, byte[] encoding)
+ {
+ if (TlsUtilities.IsNullOrEmpty(encoding))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ switch (namedGroup)
+ {
+ case NamedGroup.x25519:
+ case NamedGroup.x448:
+ return;
+ }
+
+ switch (encoding[0])
+ {
+ case 0x04: // uncompressed
+ return;
+
+ case 0x00: // infinity
+ case 0x02: // compressed
+ case 0x03: // compressed
+ case 0x06: // hybrid
+ case 0x07: // hybrid
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public static TlsECConfig ReceiveECDHConfig(TlsContext context, Stream input)
+ {
+ short curveType = TlsUtilities.ReadUint8(input);
+ if (curveType != ECCurveType.named_curve)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ int namedGroup = TlsUtilities.ReadUint16(input);
+ if (NamedGroup.RefersToAnECDHCurve(namedGroup))
+ {
+ int[] clientSupportedGroups = context.SecurityParameters.ClientSupportedGroups;
+ if (null == clientSupportedGroups || Arrays.Contains(clientSupportedGroups, namedGroup))
+ return new TlsECConfig(namedGroup);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void WriteECConfig(TlsECConfig ecConfig, Stream output)
+ {
+ WriteNamedECParameters(ecConfig.NamedGroup, output);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void WriteNamedECParameters(int namedGroup, Stream output)
+ {
+ if (!NamedGroup.RefersToASpecificCurve(namedGroup))
+ {
+ /*
+ * 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(namedGroup);
+ TlsUtilities.WriteUint16(namedGroup, output);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsException.cs b/crypto/src/tls/TlsException.cs
new file mode 100644
index 000000000..c6d7a1916
--- /dev/null
+++ b/crypto/src/tls/TlsException.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsException
+ : IOException
+ {
+ public TlsException()
+ : base()
+ {
+ }
+
+ public TlsException(string message)
+ : base(message)
+ {
+ }
+
+ public TlsException(string message, Exception cause)
+ : base(message, cause)
+ {
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsExtensionsUtilities.cs b/crypto/src/tls/TlsExtensionsUtilities.cs
new file mode 100644
index 000000000..688fee3c7
--- /dev/null
+++ b/crypto/src/tls/TlsExtensionsUtilities.cs
@@ -0,0 +1,1377 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class TlsExtensionsUtilities
+ {
+ public static IDictionary EnsureExtensionsInitialised(IDictionary extensions)
+ {
+ return extensions == null ? Platform.CreateHashtable() : extensions;
+ }
+
+ /// <param name="extensions">(Int32 -> byte[])</param>
+ /// <param name="protocolNameList">an <see cref="IList"/> of <see cref="ProtocolName"/>.</param>
+ /// <exception cref="IOException"/>
+ public static void AddAlpnExtensionClient(IDictionary extensions, IList protocolNameList)
+ {
+ extensions[ExtensionType.application_layer_protocol_negotiation] = CreateAlpnExtensionClient(protocolNameList);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddAlpnExtensionServer(IDictionary extensions, ProtocolName protocolName)
+ {
+ extensions[ExtensionType.application_layer_protocol_negotiation] = CreateAlpnExtensionServer(protocolName);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddCertificateAuthoritiesExtension(IDictionary extensions, IList authorities)
+ {
+ extensions[ExtensionType.certificate_authorities] = CreateCertificateAuthoritiesExtension(authorities);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddClientCertificateTypeExtensionClient(IDictionary extensions, short[] certificateTypes)
+ {
+ extensions[ExtensionType.client_certificate_type] = CreateCertificateTypeExtensionClient(certificateTypes);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddClientCertificateTypeExtensionServer(IDictionary extensions, short certificateType)
+ {
+ extensions[ExtensionType.client_certificate_type] = CreateCertificateTypeExtensionServer(certificateType);
+ }
+
+ public static void AddClientCertificateUrlExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.client_certificate_url] = CreateClientCertificateUrlExtension();
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddCookieExtension(IDictionary extensions, byte[] cookie)
+ {
+ extensions[ExtensionType.cookie] = CreateCookieExtension(cookie);
+ }
+
+ public static void AddEarlyDataIndication(IDictionary extensions)
+ {
+ extensions[ExtensionType.early_data] = CreateEarlyDataIndication();
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddEarlyDataMaxSize(IDictionary extensions, long maxSize)
+ {
+ extensions[ExtensionType.early_data] = CreateEarlyDataMaxSize(maxSize);
+ }
+
+ public static void AddEmptyExtensionData(IDictionary extensions, Int32 extType)
+ {
+ extensions[extType] = CreateEmptyExtensionData();
+ }
+
+ 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"/>
+ public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension)
+ {
+ extensions[ExtensionType.heartbeat] = CreateHeartbeatExtension(heartbeatExtension);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddKeyShareClientHello(IDictionary extensions, IList clientShares)
+ {
+ extensions[ExtensionType.key_share] = CreateKeyShareClientHello(clientShares);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddKeyShareHelloRetryRequest(IDictionary extensions, int namedGroup)
+ {
+ extensions[ExtensionType.key_share] = CreateKeyShareHelloRetryRequest(namedGroup);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddKeyShareServerHello(IDictionary extensions, KeyShareEntry serverShare)
+ {
+ extensions[ExtensionType.key_share] = CreateKeyShareServerHello(serverShare);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddMaxFragmentLengthExtension(IDictionary extensions, short maxFragmentLength)
+ {
+ extensions[ExtensionType.max_fragment_length] = CreateMaxFragmentLengthExtension(maxFragmentLength);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddOidFiltersExtension(IDictionary extensions, IDictionary filters)
+ {
+ extensions[ExtensionType.oid_filters] = CreateOidFiltersExtension(filters);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddPaddingExtension(IDictionary extensions, int dataLength)
+ {
+ extensions[ExtensionType.padding] = CreatePaddingExtension(dataLength);
+ }
+
+ public static void AddPostHandshakeAuthExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.post_handshake_auth] = CreatePostHandshakeAuthExtension();
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddPreSharedKeyClientHello(IDictionary extensions, OfferedPsks offeredPsks)
+ {
+ extensions[ExtensionType.pre_shared_key] = CreatePreSharedKeyClientHello(offeredPsks);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddPreSharedKeyServerHello(IDictionary extensions, int selectedIdentity)
+ {
+ extensions[ExtensionType.pre_shared_key] = CreatePreSharedKeyServerHello(selectedIdentity);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddPskKeyExchangeModesExtension(IDictionary extensions, short[] modes)
+ {
+ extensions[ExtensionType.psk_key_exchange_modes] = CreatePskKeyExchangeModesExtension(modes);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddRecordSizeLimitExtension(IDictionary extensions, int recordSizeLimit)
+ {
+ extensions[ExtensionType.record_size_limit] = CreateRecordSizeLimitExtension(recordSizeLimit);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddServerCertificateTypeExtensionClient(IDictionary extensions, short[] certificateTypes)
+ {
+ extensions[ExtensionType.server_certificate_type] = CreateCertificateTypeExtensionClient(certificateTypes);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddServerCertificateTypeExtensionServer(IDictionary extensions, short certificateType)
+ {
+ extensions[ExtensionType.server_certificate_type] = CreateCertificateTypeExtensionServer(certificateType);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddServerNameExtensionClient(IDictionary extensions, IList serverNameList)
+ {
+ extensions[ExtensionType.server_name] = CreateServerNameExtensionClient(serverNameList);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddServerNameExtensionServer(IDictionary extensions)
+ {
+ extensions[ExtensionType.server_name] = CreateServerNameExtensionServer();
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddSignatureAlgorithmsExtension(IDictionary extensions, IList supportedSignatureAlgorithms)
+ {
+ extensions[ExtensionType.signature_algorithms] = CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddSignatureAlgorithmsCertExtension(IDictionary extensions, IList supportedSignatureAlgorithms)
+ {
+ extensions[ExtensionType.signature_algorithms_cert] = CreateSignatureAlgorithmsCertExtension(supportedSignatureAlgorithms);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddStatusRequestExtension(IDictionary extensions, CertificateStatusRequest statusRequest)
+ {
+ extensions[ExtensionType.status_request] = CreateStatusRequestExtension(statusRequest);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddStatusRequestV2Extension(IDictionary extensions, IList statusRequestV2)
+ {
+ extensions[ExtensionType.status_request_v2] = CreateStatusRequestV2Extension(statusRequestV2);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddSupportedGroupsExtension(IDictionary extensions, IList namedGroups)
+ {
+ extensions[ExtensionType.supported_groups] = CreateSupportedGroupsExtension(namedGroups);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddSupportedPointFormatsExtension(IDictionary extensions, short[] ecPointFormats)
+ {
+ extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddSupportedVersionsExtensionClient(IDictionary extensions, ProtocolVersion[] versions)
+ {
+ extensions[ExtensionType.supported_versions] = CreateSupportedVersionsExtensionClient(versions);
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddSupportedVersionsExtensionServer(IDictionary extensions, ProtocolVersion selectedVersion)
+ {
+ extensions[ExtensionType.supported_versions] = CreateSupportedVersionsExtensionServer(selectedVersion);
+ }
+
+ public static void AddTruncatedHmacExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.truncated_hmac] = CreateTruncatedHmacExtension();
+ }
+
+ /// <exception cref="IOException"/>
+ public static void AddTrustedCAKeysExtensionClient(IDictionary extensions, IList trustedAuthoritiesList)
+ {
+ extensions[ExtensionType.trusted_ca_keys] = CreateTrustedCAKeysExtensionClient(trustedAuthoritiesList);
+ }
+
+ public static void AddTrustedCAKeysExtensionServer(IDictionary extensions)
+ {
+ extensions[ExtensionType.trusted_ca_keys] = CreateTrustedCAKeysExtensionServer();
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="ProtocolName"/>.</returns>
+ /// <exception cref="IOException"/>
+ public static IList GetAlpnExtensionClient(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.application_layer_protocol_negotiation);
+ return extensionData == null ? null : ReadAlpnExtensionClient(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static ProtocolName GetAlpnExtensionServer(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.application_layer_protocol_negotiation);
+ return extensionData == null ? null : ReadAlpnExtensionServer(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetCertificateAuthoritiesExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.certificate_authorities);
+ return extensionData == null ? null : ReadCertificateAuthoritiesExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] GetClientCertificateTypeExtensionClient(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.client_certificate_type);
+ return extensionData == null ? null : ReadCertificateTypeExtensionClient(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short GetClientCertificateTypeExtensionServer(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.client_certificate_type);
+ return extensionData == null ? (short)-1 : ReadCertificateTypeExtensionServer(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] GetCookieExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.cookie);
+ return extensionData == null ? null : ReadCookieExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static long GetEarlyDataMaxSize(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.early_data);
+ return extensionData == null ? -1L : ReadEarlyDataMaxSize(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static HeartbeatExtension GetHeartbeatExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.heartbeat);
+ return extensionData == null ? null : ReadHeartbeatExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetKeyShareClientHello(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.key_share);
+ return extensionData == null ? null : ReadKeyShareClientHello(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static int GetKeyShareHelloRetryRequest(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.key_share);
+ return extensionData == null ? -1 : ReadKeyShareHelloRetryRequest(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static KeyShareEntry GetKeyShareServerHello(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.key_share);
+ return extensionData == null ? null : ReadKeyShareServerHello(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short GetMaxFragmentLengthExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.max_fragment_length);
+ return extensionData == null ? (short)-1 : ReadMaxFragmentLengthExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IDictionary GetOidFiltersExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.oid_filters);
+ return extensionData == null ? null : ReadOidFiltersExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static int GetPaddingExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.padding);
+ return extensionData == null ? -1 : ReadPaddingExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static OfferedPsks GetPreSharedKeyClientHello(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.pre_shared_key);
+ return extensionData == null ? null : ReadPreSharedKeyClientHello(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static int GetPreSharedKeyServerHello(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.pre_shared_key);
+ return extensionData == null ? -1 : ReadPreSharedKeyServerHello(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] GetPskKeyExchangeModesExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.psk_key_exchange_modes);
+ return extensionData == null ? null : ReadPskKeyExchangeModesExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static int GetRecordSizeLimitExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.record_size_limit);
+ return extensionData == null ? -1 : ReadRecordSizeLimitExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] GetServerCertificateTypeExtensionClient(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_certificate_type);
+ return extensionData == null ? null : ReadCertificateTypeExtensionClient(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short GetServerCertificateTypeExtensionServer(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_certificate_type);
+ return extensionData == null ? (short)-1 : ReadCertificateTypeExtensionServer(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetServerNameExtensionClient(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name);
+ return extensionData == null ? null : ReadServerNameExtensionClient(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetSignatureAlgorithmsExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.signature_algorithms);
+ return extensionData == null ? null : ReadSignatureAlgorithmsExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetSignatureAlgorithmsCertExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.signature_algorithms_cert);
+ return extensionData == null ? null : ReadSignatureAlgorithmsCertExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static CertificateStatusRequest GetStatusRequestExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request);
+ return extensionData == null ? null : ReadStatusRequestExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetStatusRequestV2Extension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request_v2);
+ return extensionData == null ? null : ReadStatusRequestV2Extension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static int[] GetSupportedGroupsExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.supported_groups);
+ return extensionData == null ? null : ReadSupportedGroupsExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] GetSupportedPointFormatsExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
+ return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static ProtocolVersion[] GetSupportedVersionsExtensionClient(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.supported_versions);
+ return extensionData == null ? null : ReadSupportedVersionsExtensionClient(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static ProtocolVersion GetSupportedVersionsExtensionServer(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.supported_versions);
+ return extensionData == null ? null : ReadSupportedVersionsExtensionServer(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList GetTrustedCAKeysExtensionClient(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.trusted_ca_keys);
+ return extensionData == null ? null : ReadTrustedCAKeysExtensionClient(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasClientCertificateUrlExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.client_certificate_url);
+ return extensionData == null ? false : ReadClientCertificateUrlExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasEarlyDataIndication(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.early_data);
+ return extensionData == null ? false : ReadEarlyDataIndication(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasEncryptThenMacExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.encrypt_then_mac);
+ return extensionData == null ? false : ReadEncryptThenMacExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasExtendedMasterSecretExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.extended_master_secret);
+ return extensionData == null ? false : ReadExtendedMasterSecretExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasServerNameExtensionServer(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name);
+ return extensionData == null ? false : ReadServerNameExtensionServer(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasPostHandshakeAuthExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.post_handshake_auth);
+ return extensionData == null ? false : ReadPostHandshakeAuthExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasTruncatedHmacExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac);
+ return extensionData == null ? false : ReadTruncatedHmacExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool HasTrustedCAKeysExtensionServer(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.trusted_ca_keys);
+ return extensionData == null ? false : ReadTrustedCAKeysExtensionServer(extensionData);
+ }
+
+ /// <param name="protocolNameList">an <see cref="IList"/> of <see cref="ProtocolName"/>.</param>
+ /// <exception cref="IOException"/>
+ public static byte[] CreateAlpnExtensionClient(IList protocolNameList)
+ {
+ if (protocolNameList == null || protocolNameList.Count < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ // Placeholder for length
+ TlsUtilities.WriteUint16(0, buf);
+
+ foreach (ProtocolName protocolName in protocolNameList)
+ {
+ protocolName.Encode(buf);
+ }
+
+ return PatchOpaque16(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateAlpnExtensionServer(ProtocolName protocolName)
+ {
+ IList protocol_name_list = Platform.CreateArrayList();
+ protocol_name_list.Add(protocolName);
+
+ return CreateAlpnExtensionClient(protocol_name_list);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateCertificateAuthoritiesExtension(IList authorities)
+ {
+ if (null == authorities || authorities.Count < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ // Placeholder for length
+ TlsUtilities.WriteUint16(0, buf);
+
+ foreach (X509Name authority in authorities)
+ {
+ byte[] derEncoding = authority.GetEncoded(Asn1Encodable.Der);
+ TlsUtilities.WriteOpaque16(derEncoding, buf);
+ }
+
+ return PatchOpaque16(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateCertificateTypeExtensionClient(short[] certificateTypes)
+ {
+ if (TlsUtilities.IsNullOrEmpty(certificateTypes) || certificateTypes.Length > 255)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeUint8ArrayWithUint8Length(certificateTypes);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateCertificateTypeExtensionServer(short certificateType)
+ {
+ return TlsUtilities.EncodeUint8(certificateType);
+ }
+
+ public static byte[] CreateClientCertificateUrlExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateCookieExtension(byte[] cookie)
+ {
+ if (TlsUtilities.IsNullOrEmpty(cookie) || cookie.Length >= (1 << 16))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeOpaque16(cookie);
+ }
+
+ public static byte[] CreateEarlyDataIndication()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateEarlyDataMaxSize(long maxSize)
+ {
+ return TlsUtilities.EncodeUint32(maxSize);
+ }
+
+ public static byte[] CreateEmptyExtensionData()
+ {
+ return TlsUtilities.EmptyBytes;
+ }
+
+ public static byte[] CreateEncryptThenMacExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ public static byte[] CreateExtendedMasterSecretExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ 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"/>
+ public static byte[] CreateKeyShareClientHello(IList clientShares)
+ {
+ if (clientShares == null || clientShares.Count < 1)
+ return TlsUtilities.EncodeUint16(0);
+
+ MemoryStream buf = new MemoryStream();
+
+ // Placeholder for length
+ TlsUtilities.WriteUint16(0, buf);
+
+ foreach (KeyShareEntry clientShare in clientShares)
+ {
+ clientShare.Encode(buf);
+ }
+
+ return PatchOpaque16(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateKeyShareHelloRetryRequest(int namedGroup)
+ {
+ return TlsUtilities.EncodeUint16(namedGroup);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateKeyShareServerHello(KeyShareEntry serverShare)
+ {
+ if (serverShare == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ MemoryStream buf = new MemoryStream();
+
+ serverShare.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateMaxFragmentLengthExtension(short maxFragmentLength)
+ {
+ return TlsUtilities.EncodeUint8(maxFragmentLength);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateOidFiltersExtension(IDictionary filters)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ // Placeholder for length
+ TlsUtilities.WriteUint16(0, buf);
+
+ if (null != filters)
+ {
+ foreach (DerObjectIdentifier certificateExtensionOid in filters.Keys)
+ {
+ byte[] certificateExtensionValues = (byte[])filters[certificateExtensionOid];
+
+ if (null == certificateExtensionOid || null == certificateExtensionValues)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ byte[] derEncoding = certificateExtensionOid.GetEncoded(Asn1Encodable.Der);
+ TlsUtilities.WriteOpaque8(derEncoding, buf);
+
+ TlsUtilities.WriteOpaque16(certificateExtensionValues, buf);
+ }
+ }
+
+ return PatchOpaque16(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreatePaddingExtension(int dataLength)
+ {
+ TlsUtilities.CheckUint16(dataLength);
+ return new byte[dataLength];
+ }
+
+ public static byte[] CreatePostHandshakeAuthExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreatePreSharedKeyClientHello(OfferedPsks offeredPsks)
+ {
+ if (offeredPsks == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ offeredPsks.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreatePreSharedKeyServerHello(int selectedIdentity)
+ {
+ return TlsUtilities.EncodeUint16(selectedIdentity);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreatePskKeyExchangeModesExtension(short[] modes)
+ {
+ if (TlsUtilities.IsNullOrEmpty(modes) || modes.Length > 255)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeUint8ArrayWithUint8Length(modes);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateRecordSizeLimitExtension(int recordSizeLimit)
+ {
+ if (recordSizeLimit < 64)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeUint16(recordSizeLimit);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateServerNameExtensionClient(IList serverNameList)
+ {
+ if (serverNameList == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ new ServerNameList(serverNameList).Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ public static byte[] CreateServerNameExtensionServer()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSignatureAlgorithmsExtension(IList supportedSignatureAlgorithms)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ TlsUtilities.EncodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSignatureAlgorithmsCertExtension(IList supportedSignatureAlgorithms)
+ {
+ return CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms);
+ }
+
+ /// <exception cref="IOException"/>
+ 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();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateStatusRequestV2Extension(IList statusRequestV2)
+ {
+ if (statusRequestV2 == null || statusRequestV2.Count < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ // Placeholder for length
+ TlsUtilities.WriteUint16(0, buf);
+
+ foreach (CertificateStatusRequestItemV2 entry in statusRequestV2)
+ {
+ entry.Encode(buf);
+ }
+
+ return PatchOpaque16(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSupportedGroupsExtension(IList namedGroups)
+ {
+ if (namedGroups == null || namedGroups.Count < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ int count = namedGroups.Count;
+ int[] values = new int[count];
+ for (int i = 0; i < count; ++i)
+ {
+ values[i] = (Int32)namedGroups[i];
+ }
+
+ return TlsUtilities.EncodeUint16ArrayWithUint16Length(values);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSupportedPointFormatsExtension(short[] 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 start (highest preference)
+ ecPointFormats = Arrays.Prepend(ecPointFormats, ECPointFormat.uncompressed);
+ }
+
+ return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSupportedVersionsExtensionClient(ProtocolVersion[] versions)
+ {
+ if (TlsUtilities.IsNullOrEmpty(versions) || versions.Length > 127)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ int count = versions.Length;
+ byte[] data = new byte[1 + count * 2];
+ TlsUtilities.WriteUint8(count * 2, data, 0);
+ for (int i = 0; i < count; ++i)
+ {
+ TlsUtilities.WriteVersion((ProtocolVersion)versions[i], data, 1 + i * 2);
+ }
+ return data;
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSupportedVersionsExtensionServer(ProtocolVersion selectedVersion)
+ {
+ return TlsUtilities.EncodeVersion(selectedVersion);
+ }
+
+ public static byte[] CreateTruncatedHmacExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateTrustedCAKeysExtensionClient(IList trustedAuthoritiesList)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ // Placeholder for length
+ TlsUtilities.WriteUint16(0, buf);
+
+ if (trustedAuthoritiesList != null)
+ {
+ foreach (TrustedAuthority entry in trustedAuthoritiesList)
+ {
+ entry.Encode(buf);
+ }
+ }
+
+ return PatchOpaque16(buf);
+ }
+
+ public static byte[] CreateTrustedCAKeysExtensionServer()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"/>
+ 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;
+ }
+
+ /// <returns>an <see cref="IList"/> of <see cref="ProtocolName"/>.</returns>
+ /// <exception cref="IOException"/>
+ public static IList ReadAlpnExtensionClient(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData);
+
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length != (extensionData.Length - 2))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ IList protocol_name_list = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ ProtocolName protocolName = ProtocolName.Parse(buf);
+
+ protocol_name_list.Add(protocolName);
+ }
+ return protocol_name_list;
+ }
+
+ /// <exception cref="IOException"/>
+ public static ProtocolName ReadAlpnExtensionServer(byte[] extensionData)
+ {
+ IList protocol_name_list = ReadAlpnExtensionClient(extensionData);
+ if (protocol_name_list.Count != 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return (ProtocolName)protocol_name_list[0];
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList ReadCertificateAuthoritiesExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+ if (extensionData.Length < 5)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ MemoryStream buf = new MemoryStream(extensionData);
+
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length != (extensionData.Length - 2))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ IList authorities = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ byte[] derEncoding = TlsUtilities.ReadOpaque16(buf, 1);
+ Asn1Object asn1 = TlsUtilities.ReadDerObject(derEncoding);
+ authorities.Add(X509Name.GetInstance(asn1));
+ }
+ return authorities;
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] ReadCertificateTypeExtensionClient(byte[] extensionData)
+ {
+ short[] certificateTypes = TlsUtilities.DecodeUint8ArrayWithUint8Length(extensionData);
+ if (certificateTypes.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return certificateTypes;
+ }
+
+ /// <exception cref="IOException"/>
+ public static short ReadCertificateTypeExtensionServer(byte[] extensionData)
+ {
+ return TlsUtilities.DecodeUint8(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadClientCertificateUrlExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] ReadCookieExtension(byte[] extensionData)
+ {
+ return TlsUtilities.DecodeOpaque16(extensionData, 1);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadEarlyDataIndication(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static long ReadEarlyDataMaxSize(byte[] extensionData)
+ {
+ return TlsUtilities.DecodeUint32(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadEncryptThenMacExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadExtendedMasterSecretExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ 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"/>
+ public static IList ReadKeyShareClientHello(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ /*
+ * TODO[tls13] Clients MUST NOT offer multiple KeyShareEntry values for the same group.
+ * Clients MUST NOT offer any KeyShareEntry values for groups not listed in the client's
+ * "supported_groups" extension. Servers MAY check for violations of these rules and abort
+ * the handshake with an "illegal_parameter" alert if one is violated.
+ */
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length != (extensionData.Length - 2))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ IList clientShares = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ KeyShareEntry clientShare = KeyShareEntry.Parse(buf);
+
+ clientShares.Add(clientShare);
+ }
+ return clientShares;
+ }
+
+ /// <exception cref="IOException"/>
+ public static int ReadKeyShareHelloRetryRequest(byte[] extensionData)
+ {
+ return TlsUtilities.DecodeUint16(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static KeyShareEntry ReadKeyShareServerHello(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ KeyShareEntry serverShare = KeyShareEntry.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return serverShare;
+ }
+
+ /// <exception cref="IOException"/>
+ public static short ReadMaxFragmentLengthExtension(byte[] extensionData)
+ {
+ return TlsUtilities.DecodeUint8(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IDictionary ReadOidFiltersExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+ if (extensionData.Length < 2)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length != (extensionData.Length - 2))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ IDictionary filters = Platform.CreateHashtable();
+ while (buf.Position < buf.Length)
+ {
+ byte[] derEncoding = TlsUtilities.ReadOpaque8(buf, 1);
+ Asn1Object asn1 = TlsUtilities.ReadDerObject(derEncoding);
+ DerObjectIdentifier certificateExtensionOid = DerObjectIdentifier.GetInstance(asn1);
+
+ if (filters.Contains(certificateExtensionOid))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ byte[] certificateExtensionValues = TlsUtilities.ReadOpaque16(buf);
+
+ filters[certificateExtensionOid] = certificateExtensionValues;
+ }
+ return filters;
+ }
+
+ /// <exception cref="IOException"/>
+ public static int ReadPaddingExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ if (!Arrays.AreAllZeroes(extensionData, 0, extensionData.Length))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return extensionData.Length;
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadPostHandshakeAuthExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static OfferedPsks ReadPreSharedKeyClientHello(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ OfferedPsks offeredPsks = OfferedPsks.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return offeredPsks;
+ }
+
+ /// <exception cref="IOException"/>
+ public static int ReadPreSharedKeyServerHello(byte[] extensionData)
+ {
+ return TlsUtilities.DecodeUint16(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] ReadPskKeyExchangeModesExtension(byte[] extensionData)
+ {
+ short[] modes = TlsUtilities.DecodeUint8ArrayWithUint8Length(extensionData);
+ if (modes.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return modes;
+ }
+
+ /// <exception cref="IOException"/>
+ public static int ReadRecordSizeLimitExtension(byte[] extensionData)
+ {
+ int recordSizeLimit = TlsUtilities.DecodeUint16(extensionData);
+ if (recordSizeLimit < 64)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return recordSizeLimit;
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList ReadServerNameExtensionClient(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.ServerNames;
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadServerNameExtensionServer(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList ReadSignatureAlgorithmsExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ IList supported_signature_algorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return supported_signature_algorithms;
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList ReadSignatureAlgorithmsCertExtension(byte[] extensionData)
+ {
+ return ReadSignatureAlgorithmsExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ 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"/>
+ public static IList ReadStatusRequestV2Extension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+ if (extensionData.Length < 3)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length != (extensionData.Length - 2))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ IList statusRequestV2 = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ CertificateStatusRequestItemV2 entry = CertificateStatusRequestItemV2.Parse(buf);
+ statusRequestV2.Add(entry);
+ }
+ return statusRequestV2;
+ }
+
+ /// <exception cref="IOException"/>
+ public static int[] ReadSupportedGroupsExtension(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[] namedGroups = TlsUtilities.ReadUint16Array(length / 2, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return namedGroups;
+ }
+
+ /// <exception cref="IOException"/>
+ public static short[] ReadSupportedPointFormatsExtension(byte[] extensionData)
+ {
+ short[] ecPointFormats = TlsUtilities.DecodeUint8ArrayWithUint8Length(extensionData);
+ 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;
+ }
+
+ /// <exception cref="IOException"/>
+ public static ProtocolVersion[] ReadSupportedVersionsExtensionClient(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+ if (extensionData.Length < 3 || extensionData.Length > 255 || (extensionData.Length & 1) == 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int length = TlsUtilities.ReadUint8(extensionData, 0);
+ if (length != (extensionData.Length - 1))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int count = length / 2;
+ ProtocolVersion[] versions = new ProtocolVersion[count];
+ for (int i = 0; i < count; ++i)
+ {
+ versions[i] = TlsUtilities.ReadVersion(extensionData, 1 + i * 2);
+ }
+ return versions;
+ }
+
+ /// <exception cref="IOException"/>
+ public static ProtocolVersion ReadSupportedVersionsExtensionServer(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+ if (extensionData.Length != 2)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return TlsUtilities.ReadVersion(extensionData, 0);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadTruncatedHmacExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList ReadTrustedCAKeysExtensionClient(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+ if (extensionData.Length < 2)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length != (extensionData.Length - 2))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ IList trusted_authorities_list = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ TrustedAuthority entry = TrustedAuthority.Parse(buf);
+ trusted_authorities_list.Add(entry);
+ }
+ return trusted_authorities_list;
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ReadTrustedCAKeysExtensionServer(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ private static byte[] PatchOpaque16(MemoryStream buf)
+ {
+ int length = (int)buf.Length - 2;
+ TlsUtilities.CheckUint16(length);
+ byte[] extensionData = buf.ToArray();
+ TlsUtilities.WriteUint16(length, extensionData, 0);
+ return extensionData;
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsFatalAlert.cs b/crypto/src/tls/TlsFatalAlert.cs
new file mode 100644
index 000000000..9f0ea8f30
--- /dev/null
+++ b/crypto/src/tls/TlsFatalAlert.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsFatalAlert
+ : TlsException
+ {
+ private static string GetMessage(short alertDescription, string detailMessage)
+ {
+ string msg = Tls.AlertDescription.GetText(alertDescription);
+ if (null != detailMessage)
+ {
+ msg += "; " + detailMessage;
+ }
+ return msg;
+ }
+
+ protected readonly short m_alertDescription;
+
+ public TlsFatalAlert(short alertDescription)
+ : this(alertDescription, (string)null)
+ {
+ }
+
+ public TlsFatalAlert(short alertDescription, string detailMessage)
+ : this(alertDescription, detailMessage, null)
+ {
+ }
+
+ public TlsFatalAlert(short alertDescription, Exception alertCause)
+ : this(alertDescription, null, alertCause)
+ {
+ }
+
+ public TlsFatalAlert(short alertDescription, string detailMessage, Exception alertCause)
+ : base(GetMessage(alertDescription, detailMessage), alertCause)
+ {
+ this.m_alertDescription = alertDescription;
+ }
+
+ public virtual short AlertDescription
+ {
+ get { return m_alertDescription; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsFatalAlertReceived.cs b/crypto/src/tls/TlsFatalAlertReceived.cs
new file mode 100644
index 000000000..0bf6cff38
--- /dev/null
+++ b/crypto/src/tls/TlsFatalAlertReceived.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsFatalAlertReceived
+ : TlsException
+ {
+ protected readonly short m_alertDescription;
+
+ public TlsFatalAlertReceived(short alertDescription)
+ : base(Tls.AlertDescription.GetText(alertDescription))
+ {
+ this.m_alertDescription = alertDescription;
+ }
+
+ public virtual short AlertDescription
+ {
+ get { return m_alertDescription; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsHandshakeHash.cs b/crypto/src/tls/TlsHandshakeHash.cs
new file mode 100644
index 000000000..aa33c680d
--- /dev/null
+++ b/crypto/src/tls/TlsHandshakeHash.cs
@@ -0,0 +1,29 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for an object that can calculate a handshake hash.</summary>
+ public interface TlsHandshakeHash
+ : TlsHash
+ {
+ /// <exception cref="IOException"/>
+ void CopyBufferTo(Stream output);
+
+ void ForceBuffering();
+
+ void NotifyPrfDetermined();
+
+ void TrackHashAlgorithm(int cryptoHashAlgorithm);
+
+ void SealHashAlgorithms();
+
+ TlsHandshakeHash StopTracking();
+
+ TlsHash ForkPrfHash();
+
+ byte[] GetFinalHash(int cryptoHashAlgorithm);
+ }
+}
diff --git a/crypto/src/tls/TlsHeartbeat.cs b/crypto/src/tls/TlsHeartbeat.cs
new file mode 100644
index 000000000..3f208a869
--- /dev/null
+++ b/crypto/src/tls/TlsHeartbeat.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ public interface TlsHeartbeat
+ {
+ byte[] GeneratePayload();
+
+ int IdleMillis { get; }
+
+ int TimeoutMillis { get; }
+ }
+}
diff --git a/crypto/src/tls/TlsKeyExchange.cs b/crypto/src/tls/TlsKeyExchange.cs
new file mode 100644
index 000000000..42c2aa4bc
--- /dev/null
+++ b/crypto/src/tls/TlsKeyExchange.cs
@@ -0,0 +1,55 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <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);
+
+ short[] GetClientCertificateTypes();
+
+ /// <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);
+
+ bool RequiresCertificateVerify { get; }
+
+ /// <exception cref="IOException"/>
+ TlsSecret GeneratePreMasterSecret();
+ }
+}
diff --git a/crypto/src/tls/TlsKeyExchangeFactory.cs b/crypto/src/tls/TlsKeyExchangeFactory.cs
new file mode 100644
index 000000000..59b5327ab
--- /dev/null
+++ b/crypto/src/tls/TlsKeyExchangeFactory.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Interface for a key exchange factory offering a variety of specific algorithms.</summary>
+ public interface TlsKeyExchangeFactory
+ {
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateDHKeyExchange(int keyExchange);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateDHanonKeyExchangeClient(int keyExchange, TlsDHGroupVerifier dhGroupVerifier);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateDHanonKeyExchangeServer(int keyExchange, TlsDHConfig dhConfig);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateDheKeyExchangeClient(int keyExchange, TlsDHGroupVerifier dhGroupVerifier);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateDheKeyExchangeServer(int keyExchange, TlsDHConfig dhConfig);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateECDHKeyExchange(int keyExchange);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateECDHanonKeyExchangeClient(int keyExchange);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateECDHanonKeyExchangeServer(int keyExchange, TlsECConfig ecConfig);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateECDheKeyExchangeClient(int keyExchange);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateECDheKeyExchangeServer(int keyExchange, TlsECConfig ecConfig);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreatePskKeyExchangeClient(int keyExchange, TlsPskIdentity pskIdentity,
+ TlsDHGroupVerifier dhGroupVerifier);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreatePskKeyExchangeServer(int keyExchange, TlsPskIdentityManager pskIdentityManager,
+ TlsDHConfig dhConfig, TlsECConfig ecConfig);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateRsaKeyExchange(int keyExchange);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateSrpKeyExchangeClient(int keyExchange, TlsSrpIdentity srpIdentity,
+ TlsSrpConfigVerifier srpConfigVerifier);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchange CreateSrpKeyExchangeServer(int keyExchange, TlsSrpLoginParameters loginParameters);
+ }
+}
diff --git a/crypto/src/tls/TlsNoCloseNotifyException.cs b/crypto/src/tls/TlsNoCloseNotifyException.cs
new file mode 100644
index 000000000..8fdfbbfc4
--- /dev/null
+++ b/crypto/src/tls/TlsNoCloseNotifyException.cs
@@ -0,0 +1,21 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>This exception will be thrown (only) when the connection is closed by the peer without sending a
+ /// <see cref="AlertDescription.close_notify">close_notify</see> warning alert.</summary>
+ /// <remarks>
+ /// If this happens, the TLS protocol cannot rule out truncation of the connection data (potentially
+ /// malicious). It may be possible to check for truncation via some property of a higher level protocol
+ /// built upon TLS, e.g.the Content-Length header for HTTPS.
+ /// </remarks>
+ public class TlsNoCloseNotifyException
+ : EndOfStreamException
+ {
+ public TlsNoCloseNotifyException()
+ : base("No close_notify alert received before connection closed")
+ {
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsObjectIdentifiers.cs b/crypto/src/tls/TlsObjectIdentifiers.cs
new file mode 100644
index 000000000..e4bba69f7
--- /dev/null
+++ b/crypto/src/tls/TlsObjectIdentifiers.cs
@@ -0,0 +1,14 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Object Identifiers associated with TLS extensions.</summary>
+ public abstract class TlsObjectIdentifiers
+ {
+ /// <summary>RFC 7633</summary>
+ public static readonly DerObjectIdentifier id_pe_tlsfeature = X509ObjectIdentifiers.IdPE.Branch("24");
+ }
+}
diff --git a/crypto/src/tls/TlsPeer.cs b/crypto/src/tls/TlsPeer.cs
new file mode 100644
index 000000000..29b4288e2
--- /dev/null
+++ b/crypto/src/tls/TlsPeer.cs
@@ -0,0 +1,121 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for a (D)TLS endpoint.</summary>
+ public interface TlsPeer
+ {
+ TlsCrypto Crypto { get; }
+
+ void NotifyCloseHandle(TlsCloseable closehandle);
+
+ /// <exception cref="IOException"/>
+ void Cancel();
+
+ ProtocolVersion[] GetProtocolVersions();
+
+ int[] GetCipherSuites();
+
+ /// <summary>Notifies the peer that a new handshake is about to begin.</summary>
+ /// <exception cref="IOException"/>
+ void NotifyHandshakeBeginning();
+
+ /// <summary>Specify the timeout, in milliseconds, to use for the complete handshake process.</summary>
+ /// <remarks>
+ /// NOTE: Currently only respected by DTLS protocols. Negative values are not allowed. A timeout of zero means
+ /// an infinite timeout (i.e.the handshake will never time out).
+ /// </remarks>
+ /// <returns>the handshake timeout, in milliseconds.</returns>
+ int GetHandshakeTimeoutMillis();
+
+ bool AllowLegacyResumption();
+
+ int GetMaxCertificateChainLength();
+
+ int GetMaxHandshakeMessageSize();
+
+ /// <remarks>
+ /// This option is provided as a last resort for interoperability with TLS peers that fail to correctly send a
+ /// close_notify alert at end of stream. Implementations SHOULD return true; caution is advised if returning
+ /// false without a full understanding of the implications.
+ /// </remarks>
+ bool RequiresCloseNotify();
+
+ /// <remarks>This implementation supports RFC 7627 and will always negotiate the extended_master_secret
+ /// extension where possible. When connecting to a peer that does not offer/accept this extension, it is
+ /// recommended to abort the handshake.This option is provided for interoperability with legacy peers, although
+ /// some TLS features will be disabled in that case (see RFC 7627 5.4).
+ /// </remarks>
+ /// <returns><c>true</c> if the handshake should be aborted when the peer does not negotiate the
+ /// extended_master_secret extension, or <c>false</c> to support legacy interoperability.</returns>
+ bool RequiresExtendedMasterSecret();
+
+ bool ShouldUseExtendedMasterSecret();
+
+ /// <summary>See RFC 5246 6.2.3.2. Controls whether block cipher encryption may randomly add extra padding
+ /// beyond the minimum.</summary>
+ /// <remarks>
+ /// Note that in configurations where this is known to be potential security risk this setting will be ignored
+ /// (and extended padding disabled). Extra padding is always supported when decrypting received records.
+ /// </remarks>
+ /// <returns><c>true</c> if random extra padding should be added during block cipher encryption, or
+ /// <c>false</c> to always use the minimum amount of required padding.</returns>
+ bool ShouldUseExtendedPadding();
+
+ /// <summary> draft-mathewson-no-gmtunixtime-00 2. "If existing users of a TLS implementation may rely on
+ /// gmt_unix_time containing the current time, we recommend that implementors MAY provide the ability to set
+ /// gmt_unix_time as an option only, off by default.".</summary>
+ /// <remarks>
+ /// NOTE: For a server that has negotiated TLS 1.3 (or later), or a client that has offered TLS 1.3 (or later),
+ /// this is not called and gmt_unix_time is not used.
+ /// </remarks>
+ /// <returns><c>true</c> if the current time should be used in the gmt_unix_time field of Random, or
+ /// <c>false</c> if gmt_unix_time should contain a cryptographically random value.</returns>
+ bool ShouldUseGmtUnixTime();
+
+ /// <summary>RFC 5746 3.4/3.6. In case this is false, peers may want to terminate the handshake instead of
+ /// continuing; see Section 4.1/4.3 for discussion.</summary>
+ /// <remarks>
+ /// NOTE: TLS 1.3 forbids renegotiation, so this is never called when TLS 1.3 (or later) was negotiated.
+ /// </remarks>
+ /// <exception cref="IOException"/>
+ void NotifySecureRenegotiation(bool secureRenegotiation);
+
+ /// <exception cref="IOException"/>
+ TlsKeyExchangeFactory GetKeyExchangeFactory();
+
+ /// <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 <see cref="Exception"/> that caused this alert to be raised. May be null.</param>
+ void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short alertDescription);
+
+ /// <summary>Notifies the peer that the handshake has been successfully completed.</summary>
+ /// <exception cref="IOException"/>
+ void NotifyHandshakeComplete();
+
+ /// <summary>Return a <see cref="TlsHeartbeat"/> instance that will control the generation of heartbeats
+ /// locally (if permitted by the remote peer), or null to not generate heartbeats. Heartbeats are described in
+ /// RFC 6520.</summary>
+ /// <returns>an instance of <see cref="TlsHeartbeat"/>.</returns>
+ /// <seealso cref="DefaultTlsHeartbeat"/>
+ TlsHeartbeat GetHeartbeat();
+
+ /// <summary>Return the heartbeat mode applicable to the remote peer. Heartbeats are described in RFC 6520.
+ /// </summary>
+ /// <remarks>
+ /// See enumeration class <see cref="HeartbeatMode"/> for appropriate return values.
+ /// </remarks>
+ /// <returns>the <see cref="HeartbeatMode"/> value.</returns>
+ short GetHeartbeatPolicy();
+ }
+}
diff --git a/crypto/src/tls/TlsProtocol.cs b/crypto/src/tls/TlsProtocol.cs
new file mode 100644
index 000000000..db30f6b40
--- /dev/null
+++ b/crypto/src/tls/TlsProtocol.cs
@@ -0,0 +1,1867 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class TlsProtocol
+ : TlsCloseable
+ {
+ /*
+ * Connection States.
+ *
+ * NOTE: Redirection of handshake messages to TLS 1.3 handlers assumes CS_START, CS_CLIENT_HELLO
+ * are lower than any of the other values.
+ */
+ protected const short CS_START = 0;
+ protected const short CS_CLIENT_HELLO = 1;
+ protected const short CS_SERVER_HELLO_RETRY_REQUEST = 2;
+ protected const short CS_CLIENT_HELLO_RETRY = 3;
+ protected const short CS_SERVER_HELLO = 4;
+ protected const short CS_SERVER_ENCRYPTED_EXTENSIONS = 5;
+ protected const short CS_SERVER_SUPPLEMENTAL_DATA = 6;
+ protected const short CS_SERVER_CERTIFICATE = 7;
+ protected const short CS_SERVER_CERTIFICATE_STATUS = 8;
+ protected const short CS_SERVER_CERTIFICATE_VERIFY = 9;
+ protected const short CS_SERVER_KEY_EXCHANGE = 10;
+ protected const short CS_SERVER_CERTIFICATE_REQUEST = 11;
+ protected const short CS_SERVER_HELLO_DONE = 12;
+ protected const short CS_CLIENT_END_OF_EARLY_DATA = 13;
+ protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 14;
+ protected const short CS_CLIENT_CERTIFICATE = 15;
+ protected const short CS_CLIENT_KEY_EXCHANGE = 16;
+ protected const short CS_CLIENT_CERTIFICATE_VERIFY = 17;
+ protected const short CS_CLIENT_FINISHED = 18;
+ protected const short CS_SERVER_SESSION_TICKET = 19;
+ protected const short CS_SERVER_FINISHED = 20;
+ protected const short CS_END = 21;
+
+ protected bool IsLegacyConnectionState()
+ {
+ switch (m_connectionState)
+ {
+ case CS_START:
+ case CS_CLIENT_HELLO:
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_SERVER_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ case CS_SERVER_CERTIFICATE_REQUEST:
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ case CS_CLIENT_CERTIFICATE:
+ case CS_CLIENT_KEY_EXCHANGE:
+ case CS_CLIENT_CERTIFICATE_VERIFY:
+ case CS_CLIENT_FINISHED:
+ case CS_SERVER_SESSION_TICKET:
+ case CS_SERVER_FINISHED:
+ case CS_END:
+ return true;
+
+ case CS_SERVER_HELLO_RETRY_REQUEST:
+ case CS_CLIENT_HELLO_RETRY:
+ case CS_SERVER_ENCRYPTED_EXTENSIONS:
+ case CS_SERVER_CERTIFICATE_VERIFY:
+ case CS_CLIENT_END_OF_EARLY_DATA:
+ default:
+ return false;
+ }
+ }
+
+ protected bool IsTlsV13ConnectionState()
+ {
+ switch (m_connectionState)
+ {
+ case CS_START:
+ case CS_CLIENT_HELLO:
+ case CS_SERVER_HELLO_RETRY_REQUEST:
+ case CS_CLIENT_HELLO_RETRY:
+ case CS_SERVER_HELLO:
+ case CS_SERVER_ENCRYPTED_EXTENSIONS:
+ case CS_SERVER_CERTIFICATE_REQUEST:
+ case CS_SERVER_CERTIFICATE:
+ case CS_SERVER_CERTIFICATE_VERIFY:
+ case CS_SERVER_FINISHED:
+ case CS_CLIENT_END_OF_EARLY_DATA:
+ case CS_CLIENT_CERTIFICATE:
+ case CS_CLIENT_CERTIFICATE_VERIFY:
+ case CS_CLIENT_FINISHED:
+ case CS_END:
+ return true;
+
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ case CS_CLIENT_KEY_EXCHANGE:
+ case CS_SERVER_SESSION_TICKET:
+ default:
+ return false;
+ }
+ }
+
+ /*
+ * Different modes to handle the known IV weakness
+ */
+ protected const short ADS_MODE_1_Nsub1 = 0; // 1/n-1 record splitting
+ protected const short ADS_MODE_0_N = 1; // 0/n record splitting
+ protected const short ADS_MODE_0_N_FIRSTONLY = 2; // 0/n record splitting on first data fragment only
+
+ /*
+ * Queues for data from some protocols.
+ */
+ private readonly ByteQueue m_applicationDataQueue = new ByteQueue(0);
+ private readonly ByteQueue m_alertQueue = new ByteQueue(2);
+ private readonly ByteQueue m_handshakeQueue = new ByteQueue(0);
+ //private readonly ByteQueue m_heartbeatQueue = new ByteQueue(0);
+
+ internal readonly RecordStream m_recordStream;
+ internal readonly object m_recordWriteLock = new object();
+
+ private int m_maxHandshakeMessageSize = -1;
+
+ internal TlsHandshakeHash m_handshakeHash;
+
+ private TlsStream m_tlsStream = null;
+
+ private volatile bool m_closed = false;
+ private volatile bool m_failedWithError = false;
+ private volatile bool m_appDataReady = false;
+ private volatile bool m_appDataSplitEnabled = true;
+ private volatile bool m_keyUpdateEnabled = false;
+ //private volatile bool m_keyUpdatePendingReceive = false;
+ private volatile bool m_keyUpdatePendingSend = false;
+ private volatile bool m_resumableHandshake = false;
+ private volatile int m_appDataSplitMode = ADS_MODE_1_Nsub1;
+
+ protected TlsSession m_tlsSession = null;
+ protected SessionParameters m_sessionParameters = null;
+ protected TlsSecret m_sessionMasterSecret = null;
+
+ protected byte[] m_retryCookie = null;
+ protected int m_retryGroup = -1;
+ protected IDictionary m_clientExtensions = null;
+ protected IDictionary m_serverExtensions = null;
+
+ protected short m_connectionState = CS_START;
+ protected bool m_resumedSession = false;
+ protected bool m_receivedChangeCipherSpec = false;
+ protected bool m_expectSessionTicket = false;
+
+ protected readonly bool m_blocking;
+ protected readonly ByteQueueInputStream m_inputBuffers;
+ protected readonly ByteQueueOutputStream m_outputBuffer;
+
+ protected TlsProtocol()
+ {
+ this.m_blocking = false;
+ this.m_inputBuffers = new ByteQueueInputStream();
+ this.m_outputBuffer = new ByteQueueOutputStream();
+ this.m_recordStream = new RecordStream(this, m_inputBuffers, m_outputBuffer);
+ }
+
+ public TlsProtocol(Stream stream)
+ : this(stream, stream)
+ {
+ }
+
+ public TlsProtocol(Stream input, Stream output)
+ {
+ this.m_blocking = true;
+ this.m_inputBuffers = null;
+ this.m_outputBuffer = null;
+ this.m_recordStream = new RecordStream(this, input, output);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void ResumeHandshake()
+ {
+ if (!m_blocking)
+ throw new InvalidOperationException("Cannot use ResumeHandshake() in non-blocking mode!");
+ if (!IsHandshaking)
+ throw new InvalidOperationException("No handshake in progress");
+
+ BlockForHandshake();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void CloseConnection()
+ {
+ m_recordStream.Close();
+ }
+
+ protected abstract TlsContext Context { get; }
+
+ internal abstract AbstractTlsContext ContextAdmin { get; }
+
+ protected abstract TlsPeer Peer { get; }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleAlertMessage(short alertLevel, short alertDescription)
+ {
+ Peer.NotifyAlertReceived(alertLevel, alertDescription);
+
+ if (alertLevel == AlertLevel.warning)
+ {
+ HandleAlertWarningMessage(alertDescription);
+ }
+ else
+ {
+ HandleFailure();
+
+ throw new TlsFatalAlertReceived(alertDescription);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleAlertWarningMessage(short alertDescription)
+ {
+ switch (alertDescription)
+ {
+ /*
+ * 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.
+ */
+ case AlertDescription.close_notify:
+ {
+ if (!m_appDataReady)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ HandleClose(false);
+ break;
+ }
+ case AlertDescription.no_certificate:
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ case AlertDescription.no_renegotiation:
+ {
+ // TODO[reneg] Give peer the option to tolerate this
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleChangeCipherSpecMessage()
+ {
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleClose(bool user_canceled)
+ {
+ if (!m_closed)
+ {
+ this.m_closed = true;
+
+ if (user_canceled && !m_appDataReady)
+ {
+ RaiseAlertWarning(AlertDescription.user_canceled, "User canceled handshake");
+ }
+
+ RaiseAlertWarning(AlertDescription.close_notify, "Connection closed");
+
+ if (!m_appDataReady)
+ {
+ CleanupHandshake();
+ }
+
+ CloseConnection();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleException(short alertDescription, string message, Exception e)
+ {
+ // TODO[tls-port] Can we support interrupted IO on .NET?
+ //if ((m_appDataReady || IsResumableHandshake()) && (e is InterruptedIOException))
+ // return;
+
+ if (!m_closed)
+ {
+ RaiseAlertFatal(alertDescription, message, e);
+
+ HandleFailure();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void HandleFailure()
+ {
+ this.m_closed = true;
+ this.m_failedWithError = true;
+
+ /*
+ * 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();
+
+ if (!m_appDataReady)
+ {
+ CleanupHandshake();
+ }
+
+ CloseConnection();
+ }
+
+ /// <exception cref="IOException"/>
+ protected abstract void HandleHandshakeMessage(short type, HandshakeMessageInput buf);
+
+ /// <exception cref="IOException"/>
+ protected virtual void ApplyMaxFragmentLengthExtension(short maxFragmentLength)
+ {
+ if (maxFragmentLength >= 0)
+ {
+ if (!MaxFragmentLength.IsValid(maxFragmentLength))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ int plainTextLimit = 1 << (8 + maxFragmentLength);
+ m_recordStream.SetPlaintextLimit(plainTextLimit);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void CheckReceivedChangeCipherSpec(bool expected)
+ {
+ if (expected != m_receivedChangeCipherSpec)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void BlockForHandshake()
+ {
+ while (m_connectionState != CS_END)
+ {
+ if (IsClosed)
+ {
+ // NOTE: Any close during the handshake should have raised an exception.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ SafeReadRecord();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void BeginHandshake()
+ {
+ AbstractTlsContext context = ContextAdmin;
+ TlsPeer peer = Peer;
+
+ this.m_maxHandshakeMessageSize = System.Math.Max(1024, peer.GetMaxHandshakeMessageSize());
+
+ this.m_handshakeHash = new DeferredHash(context);
+ this.m_connectionState = CS_START;
+
+ context.HandshakeBeginning(peer);
+
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ securityParameters.m_extendedPadding = peer.ShouldUseExtendedPadding();
+ }
+
+ protected virtual void CleanupHandshake()
+ {
+ TlsContext context = Context;
+ if (null != context)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ if (null != securityParameters)
+ {
+ securityParameters.Clear();
+ }
+ }
+
+ this.m_tlsSession = null;
+ this.m_sessionParameters = null;
+ this.m_sessionMasterSecret = null;
+
+ this.m_retryCookie = null;
+ this.m_retryGroup = -1;
+ this.m_clientExtensions = null;
+ this.m_serverExtensions = null;
+
+ this.m_resumedSession = false;
+ this.m_receivedChangeCipherSpec = false;
+ this.m_expectSessionTicket = false;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void CompleteHandshake()
+ {
+ try
+ {
+ AbstractTlsContext context = ContextAdmin;
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ if (m_appDataReady ||
+ null == securityParameters.LocalVerifyData ||
+ null == securityParameters.PeerVerifyData)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ m_recordStream.FinaliseHandshake();
+ this.m_connectionState = CS_END;
+
+ // TODO Prefer to set to null, but would need guards elsewhere
+ this.m_handshakeHash = new DeferredHash(context);
+
+ m_alertQueue.Shrink();
+ m_handshakeQueue.Shrink();
+
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ this.m_appDataSplitEnabled = !TlsUtilities.IsTlsV11(negotiatedVersion);
+ this.m_appDataReady = true;
+
+ this.m_keyUpdateEnabled = TlsUtilities.IsTlsV13(negotiatedVersion);
+
+ if (m_blocking)
+ {
+ this.m_tlsStream = new TlsStream(this);
+ }
+
+ if (m_sessionParameters == null)
+ {
+ this.m_sessionMasterSecret = securityParameters.MasterSecret;
+
+ this.m_sessionParameters = new SessionParameters.Builder()
+ .SetCipherSuite(securityParameters.CipherSuite)
+ .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
+ .SetLocalCertificate(securityParameters.LocalCertificate)
+ .SetMasterSecret(context.Crypto.AdoptSecret(m_sessionMasterSecret))
+ .SetNegotiatedVersion(securityParameters.NegotiatedVersion)
+ .SetPeerCertificate(securityParameters.PeerCertificate)
+ .SetPskIdentity(securityParameters.PskIdentity)
+ .SetSrpIdentity(securityParameters.SrpIdentity)
+ // TODO Consider filtering extensions that aren't relevant to resumed sessions
+ .SetServerExtensions(m_serverExtensions)
+ .Build();
+
+ this.m_tlsSession = TlsUtilities.ImportSession(m_tlsSession.SessionID, m_sessionParameters);
+ }
+ else
+ {
+ securityParameters.m_localCertificate = m_sessionParameters.LocalCertificate;
+ securityParameters.m_peerCertificate = m_sessionParameters.PeerCertificate;
+ securityParameters.m_pskIdentity = m_sessionParameters.PskIdentity;
+ securityParameters.m_srpIdentity = m_sessionParameters.SrpIdentity;
+ }
+
+ context.HandshakeComplete(Peer, m_tlsSession);
+ }
+ finally
+ {
+ CleanupHandshake();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal void ProcessRecord(short protocol, byte[] buf, int off, int len)
+ {
+ /*
+ * Have a look at the protocol type, and add it to the correct queue.
+ */
+ switch (protocol)
+ {
+ case ContentType.alert:
+ {
+ m_alertQueue.AddData(buf, off, len);
+ ProcessAlertQueue();
+ break;
+ }
+ case ContentType.application_data:
+ {
+ if (!m_appDataReady)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ m_applicationDataQueue.AddData(buf, off, len);
+ ProcessApplicationDataQueue();
+ break;
+ }
+ case ContentType.change_cipher_spec:
+ {
+ ProcessChangeCipherSpec(buf, off, len);
+ break;
+ }
+ case ContentType.handshake:
+ {
+ if (m_handshakeQueue.Available > 0)
+ {
+ m_handshakeQueue.AddData(buf, off, len);
+ ProcessHandshakeQueue(m_handshakeQueue);
+ }
+ else
+ {
+ ByteQueue tmpQueue = new ByteQueue(buf, off, len);
+ ProcessHandshakeQueue(tmpQueue);
+ int remaining = tmpQueue.Available;
+ if (remaining > 0)
+ {
+ m_handshakeQueue.AddData(buf, off + len - remaining, remaining);
+ }
+ }
+ break;
+ }
+ //case ContentType.heartbeat:
+ //{
+ // if (!m_appDataReady)
+ // throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ // // TODO[RFC 6520]
+ // m_heartbeatQueue.addData(buf, off, len);
+ // ProcessHeartbeatQueue();
+ // break;
+ //}
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private void ProcessHandshakeQueue(ByteQueue queue)
+ {
+ /*
+ * We need the first 4 bytes, they contain type and length of the message.
+ */
+ while (queue.Available >= 4)
+ {
+ int header = queue.ReadInt32();
+
+ short type = (short)((uint)header >> 24);
+ if (!HandshakeType.IsRecognized(type))
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message,
+ "Handshake message of unrecognized type: " + type);
+ }
+
+ int length = header & 0x00FFFFFF;
+ if (length > m_maxHandshakeMessageSize)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error,
+ "Handshake message length exceeds the maximum: " + HandshakeType.GetText(type) + ", " + length
+ + " > " + m_maxHandshakeMessageSize);
+ }
+
+ int totalLength = 4 + length;
+ if (queue.Available < totalLength)
+ {
+ // Not enough bytes in the buffer to read the full message.
+ break;
+ }
+
+ /*
+ * Check ChangeCipherSpec status
+ */
+ switch (type)
+ {
+ case HandshakeType.hello_request:
+ break;
+
+ default:
+ {
+ ProtocolVersion negotiatedVersion = Context.ServerVersion;
+ if (null != negotiatedVersion && TlsUtilities.IsTlsV13(negotiatedVersion))
+ break;
+
+ CheckReceivedChangeCipherSpec(HandshakeType.finished == type);
+ break;
+ }
+ }
+
+ HandshakeMessageInput buf = queue.ReadHandshakeMessage(totalLength);
+
+ switch (type)
+ {
+ /*
+ * These message types aren't included in the transcript.
+ */
+ case HandshakeType.hello_request:
+ case HandshakeType.key_update:
+ case HandshakeType.new_session_ticket:
+ break;
+
+ /*
+ * These message types are deferred to the handler to explicitly update the transcript.
+ */
+ case HandshakeType.certificate_verify:
+ case HandshakeType.client_hello:
+ case HandshakeType.finished:
+ case HandshakeType.server_hello:
+ break;
+
+ /*
+ * For all others we automatically update the transcript immediately.
+ */
+ default:
+ {
+ buf.UpdateHash(m_handshakeHash);
+ break;
+ }
+ }
+
+ buf.Seek(4L, SeekOrigin.Current);
+
+ HandleHandshakeMessage(type, buf);
+ }
+ }
+
+ private void ProcessApplicationDataQueue()
+ {
+ /*
+ * There is nothing we need to do here.
+ *
+ * This function could be used for callbacks when application data arrives in the future.
+ */
+ }
+
+ /// <exception cref="IOException"/>
+ private void ProcessAlertQueue()
+ {
+ while (m_alertQueue.Available >= 2)
+ {
+ /*
+ * An alert is always 2 bytes. Read the alert.
+ */
+ byte[] alert = m_alertQueue.RemoveData(2, 0);
+ short alertLevel = alert[0];
+ short alertDescription = alert[1];
+
+ HandleAlertMessage(alertLevel, alertDescription);
+ }
+ }
+
+ /// <summary>This method is called, when a change cipher spec message is received.</summary>
+ /// <exception cref="IOException">If the message has an invalid content or the handshake is not in the correct
+ /// state.</exception>
+ private void ProcessChangeCipherSpec(byte[] buf, int off, int len)
+ {
+ ProtocolVersion negotiatedVersion = Context.ServerVersion;
+ if (null == negotiatedVersion || TlsUtilities.IsTlsV13(negotiatedVersion))
+ {
+ // See RFC 8446 D.4.
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ for (int i = 0; i < len; ++i)
+ {
+ short message = TlsUtilities.ReadUint8(buf, off + i);
+
+ if (message != ChangeCipherSpec.change_cipher_spec)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ if (this.m_receivedChangeCipherSpec
+ || m_alertQueue.Available > 0
+ || m_handshakeQueue.Available > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ m_recordStream.NotifyChangeCipherSpecReceived();
+
+ this.m_receivedChangeCipherSpec = true;
+
+ HandleChangeCipherSpecMessage();
+ }
+ }
+
+ public virtual int ApplicationDataAvailable
+ {
+ get { return m_applicationDataQueue.Available; }
+ }
+
+ /// <summary>Read data from the network.</summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ /// <param name="buf">The buffer where the data will be copied to.</param>
+ /// <param name="off">The position where the data will be placed in the buffer.</param>
+ /// <param name="len">The maximum number of bytes to read.</param>
+ /// <returns>The number of bytes read.</returns>
+ /// <exception cref="IOException">If something goes wrong during reading data.</exception>
+ public virtual int ReadApplicationData(byte[] buf, int off, int len)
+ {
+ if (len < 1)
+ return 0;
+
+ while (m_applicationDataQueue.Available == 0)
+ {
+ if (this.m_closed)
+ {
+ if (this.m_failedWithError)
+ throw new IOException("Cannot read application data on failed TLS connection");
+
+ return -1;
+ }
+ if (!m_appDataReady)
+ throw new InvalidOperationException("Cannot read application data until initial handshake completed.");
+
+ /*
+ * NOTE: Only called more than once when empty records are received, so no special
+ * InterruptedIOException handling is necessary.
+ */
+ SafeReadRecord();
+ }
+
+ len = System.Math.Min(len, m_applicationDataQueue.Available);
+ m_applicationDataQueue.RemoveData(buf, off, len, 0);
+ return len;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual RecordPreview SafePreviewRecordHeader(byte[] recordHeader)
+ {
+ try
+ {
+ return m_recordStream.PreviewRecordHeader(recordHeader);
+ }
+ catch (TlsFatalAlert e)
+ {
+ HandleException(e.AlertDescription, "Failed to read record", e);
+ throw e;
+ }
+ catch (IOException e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to read record", e);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to read record", e);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SafeReadRecord()
+ {
+ try
+ {
+ if (m_recordStream.ReadRecord())
+ return;
+
+ if (!m_appDataReady)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ if (!Peer.RequiresCloseNotify())
+ {
+ HandleClose(false);
+ return;
+ }
+ }
+ catch (TlsFatalAlertReceived e)
+ {
+ // Connection failure already handled at source
+ throw e;
+ }
+ catch (TlsFatalAlert e)
+ {
+ HandleException(e.AlertDescription, "Failed to read record", e);
+ throw e;
+ }
+ catch (IOException e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to read record", e);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to read record", e);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+
+ HandleFailure();
+
+ throw new TlsNoCloseNotifyException();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual bool SafeReadFullRecord(byte[] input, int inputOff, int inputLen)
+ {
+ try
+ {
+ return m_recordStream.ReadFullRecord(input, inputOff, inputLen);
+ }
+ catch (TlsFatalAlert e)
+ {
+ HandleException(e.AlertDescription, "Failed to process record", e);
+ throw e;
+ }
+ catch (IOException e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to process record", e);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to process record", e);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SafeWriteRecord(short type, byte[] buf, int offset, int len)
+ {
+ try
+ {
+ m_recordStream.WriteRecord(type, buf, offset, len);
+ }
+ catch (TlsFatalAlert e)
+ {
+ HandleException(e.AlertDescription, "Failed to write record", e);
+ throw e;
+ }
+ catch (IOException e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to write record", e);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ HandleException(AlertDescription.internal_error, "Failed to write record", e);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+
+ /// <summary>Write some application data.</summary>
+ /// <remarks>
+ /// Fragmentation is handled internally. Usable in both blocking/non-blocking modes.<br/><br/>
+ /// In blocking mode, the output will be automatically sent via the underlying transport. In non-blocking mode,
+ /// call <see cref="ReadOutput(byte[], int, int)"/> to get the output bytes to send to the peer.<br/><br/>
+ /// This method must not be called until after the initial handshake is complete. Attempting to call it earlier
+ /// will result in an <see cref="InvalidOperationException"/>.
+ /// </remarks>
+ /// <param name="buf">The buffer containing application data to send.</param>
+ /// <param name="off">The offset at which the application data begins</param>
+ /// <param name="len">The number of bytes of application data.</param>
+ /// <exception cref="InvalidOperationException">If called before the initial handshake has completed.
+ /// </exception>
+ /// <exception cref="IOException">If connection is already closed, or for encryption or transport errors.
+ /// </exception>
+ public virtual void WriteApplicationData(byte[] buf, int off, int len)
+ {
+ if (!m_appDataReady)
+ throw new InvalidOperationException(
+ "Cannot write application data until initial handshake completed.");
+
+ lock (m_recordWriteLock)
+ {
+ while (len > 0)
+ {
+ if (m_closed)
+ throw new IOException("Cannot write application data on closed/failed TLS connection");
+
+ /*
+ * 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 (m_appDataSplitEnabled)
+ {
+ /*
+ * Protect against known IV attack!
+ *
+ * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+ */
+ switch (m_appDataSplitMode)
+ {
+ case ADS_MODE_0_N_FIRSTONLY:
+ {
+ this.m_appDataSplitEnabled = false;
+ SafeWriteRecord(ContentType.application_data, TlsUtilities.EmptyBytes, 0, 0);
+ break;
+ }
+ case ADS_MODE_0_N:
+ {
+ SafeWriteRecord(ContentType.application_data, TlsUtilities.EmptyBytes, 0, 0);
+ break;
+ }
+ case ADS_MODE_1_Nsub1:
+ default:
+ {
+ if (len > 1)
+ {
+ SafeWriteRecord(ContentType.application_data, buf, off, 1);
+ ++off;
+ --len;
+ }
+ break;
+ }
+ }
+ }
+ else if (m_keyUpdateEnabled)
+ {
+ if (m_keyUpdatePendingSend)
+ {
+ Send13KeyUpdate(false);
+ }
+ else if (m_recordStream.NeedsKeyUpdate())
+ {
+ Send13KeyUpdate(true);
+ }
+ }
+
+ // Fragment data according to the current fragment limit.
+ int toWrite = System.Math.Min(len, m_recordStream.PlaintextLimit);
+ SafeWriteRecord(ContentType.application_data, buf, off, toWrite);
+ off += toWrite;
+ len -= toWrite;
+ }
+ }
+ }
+
+ public virtual int AppDataSplitMode
+ {
+ get { return m_appDataSplitMode; }
+ set
+ {
+ if (value < ADS_MODE_1_Nsub1 || value > ADS_MODE_0_N_FIRSTONLY)
+ throw new InvalidOperationException("Illegal appDataSplitMode mode: " + value);
+
+ this.m_appDataSplitMode = value;
+ }
+ }
+
+ public virtual bool IsResumableHandshake
+ {
+ get { return m_resumableHandshake; }
+ set { this.m_resumableHandshake = value; }
+ }
+
+ /// <exception cref="IOException"/>
+ internal void WriteHandshakeMessage(byte[] buf, int off, int len)
+ {
+ if (len < 4)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ short type = TlsUtilities.ReadUint8(buf, off);
+ switch (type)
+ {
+ case HandshakeType.hello_request:
+ case HandshakeType.key_update:
+ case HandshakeType.new_session_ticket:
+ break;
+
+ default:
+ {
+ m_handshakeHash.Update(buf, off, len);
+ break;
+ }
+ }
+
+ int total = 0;
+ do
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = System.Math.Min(len - total, m_recordStream.PlaintextLimit);
+ SafeWriteRecord(ContentType.handshake, buf, off + total, toWrite);
+ total += toWrite;
+ }
+ while (total < len);
+ }
+
+ /// <summary>The secure bidirectional stream for this connection</summary>
+ /// <remarks>Only allowed in blocking mode.</remarks>
+ public virtual Stream Stream
+ {
+ get
+ {
+ if (!m_blocking)
+ throw new InvalidOperationException(
+ "Cannot use Stream in non-blocking mode! Use OfferInput()/OfferOutput() instead.");
+
+ return this.m_tlsStream;
+ }
+ }
+
+ /// <summary>Should be called in non-blocking mode when the input data reaches EOF.</summary>
+ /// <exception cref="IOException"/>
+ public virtual void CloseInput()
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use CloseInput() in blocking mode!");
+
+ if (m_closed)
+ return;
+
+ if (m_inputBuffers.Available > 0)
+ throw new EndOfStreamException();
+
+ if (!m_appDataReady)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ if (!Peer.RequiresCloseNotify())
+ {
+ HandleClose(false);
+ return;
+ }
+
+ HandleFailure();
+
+ throw new TlsNoCloseNotifyException();
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual RecordPreview PreviewInputRecord(byte[] recordHeader)
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use PreviewInputRecord() in blocking mode!");
+ if (m_inputBuffers.Available != 0)
+ throw new InvalidOperationException("Can only use PreviewInputRecord() for record-aligned input.");
+ if (m_closed)
+ throw new IOException("Connection is closed, cannot accept any more input");
+
+ return SafePreviewRecordHeader(recordHeader);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual RecordPreview PreviewOutputRecord(int applicationDataSize)
+ {
+ if (!m_appDataReady)
+ throw new InvalidOperationException(
+ "Cannot use PreviewOutputRecord() until initial handshake completed.");
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use PreviewOutputRecord() in blocking mode!");
+ if (m_outputBuffer.Buffer.Available != 0)
+ throw new InvalidOperationException("Can only use PreviewOutputRecord() for record-aligned output.");
+ if (m_closed)
+ throw new IOException("Connection is closed, cannot produce any more output");
+
+ if (applicationDataSize < 1)
+ return new RecordPreview(0, 0);
+
+ if (m_appDataSplitEnabled)
+ {
+ switch (m_appDataSplitMode)
+ {
+ case ADS_MODE_0_N_FIRSTONLY:
+ case ADS_MODE_0_N:
+ {
+ RecordPreview a = m_recordStream.PreviewOutputRecord(0);
+ RecordPreview b = m_recordStream.PreviewOutputRecord(applicationDataSize);
+ return RecordPreview.CombineAppData(a, b);
+ }
+ case ADS_MODE_1_Nsub1:
+ default:
+ {
+ RecordPreview a = m_recordStream.PreviewOutputRecord(1);
+ if (applicationDataSize > 1)
+ {
+ RecordPreview b = m_recordStream.PreviewOutputRecord(applicationDataSize - 1);
+ a = RecordPreview.CombineAppData(a, b);
+ }
+ return a;
+ }
+ }
+ }
+ else
+ {
+ RecordPreview a = m_recordStream.PreviewOutputRecord(applicationDataSize);
+ if (m_keyUpdateEnabled && (m_keyUpdatePendingSend || m_recordStream.NeedsKeyUpdate()))
+ {
+ int keyUpdateLength = HandshakeMessageOutput.GetLength(1);
+ int recordSize = m_recordStream.PreviewOutputRecordSize(keyUpdateLength);
+ a = RecordPreview.ExtendRecordSize(a, recordSize);
+ }
+ return a;
+ }
+ }
+
+ /// <summary>Equivalent to <code>OfferInput(input, 0, input.Length)</code>.</summary>
+ /// <param name="input">The input buffer to offer.</param>
+ /// <exception cref="IOException"/>
+ /// <seealso cref="OfferInput(byte[], int, int)"/>
+ public virtual void OfferInput(byte[] input)
+ {
+ OfferInput(input, 0, input.Length);
+ }
+
+ /// <summary>Offer input from an arbitrary source.</summary>
+ /// <remarks>Only allowed in non-blocking mode.<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
+ /// <see cref="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 <see cref="GetAvailableOutputBytes"/>.
+ /// </remarks>
+ /// <param name="input">The input buffer to offer.</param>
+ /// <param name="inputOff">The offset within the input buffer that input begins.</param>
+ /// <param name="inputLen">The number of bytes of input being offered.</param>
+ /// <exception cref="IOException">If an error occurs while decrypting or processing a record.</exception>
+ public virtual void OfferInput(byte[] input, int inputOff, int inputLen)
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use OfferInput() in blocking mode! Use Stream instead.");
+ if (m_closed)
+ throw new IOException("Connection is closed, cannot accept any more input");
+
+ // Fast path if the input is arriving one record at a time
+ if (m_inputBuffers.Available == 0 && SafeReadFullRecord(input, inputOff, inputLen))
+ {
+ if (m_closed)
+ {
+ if (!m_appDataReady)
+ {
+ // NOTE: Any close during the handshake should have raised an exception.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+ return;
+ }
+
+ m_inputBuffers.AddBytes(input, inputOff, inputLen);
+
+ // loop while there are enough bytes to read the length of the next record
+ while (m_inputBuffers.Available >= RecordFormat.FragmentOffset)
+ {
+ byte[] recordHeader = new byte[RecordFormat.FragmentOffset];
+ if (RecordFormat.FragmentOffset != m_inputBuffers.Peek(recordHeader))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ RecordPreview preview = SafePreviewRecordHeader(recordHeader);
+ if (m_inputBuffers.Available < preview.RecordSize)
+ {
+ // not enough bytes to read a whole record
+ break;
+ }
+
+ // NOTE: This is actually reading from inputBuffers, so InterruptedIOException shouldn't be possible
+ SafeReadRecord();
+
+ if (m_closed)
+ {
+ if (!m_appDataReady)
+ {
+ // NOTE: Any close during the handshake should have raised an exception.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ break;
+ }
+ }
+ }
+
+ public virtual int ApplicationDataLimit
+ {
+ get { return m_recordStream.PlaintextLimit; }
+ }
+
+ /// <summary>Gets the amount of received application data.</summary>
+ /// <remarks>A call to <see cref="readInput(byte[], int, int)"/> is guaranteed to be able to return at least
+ /// this much data.<br/><br/>
+ /// Only allowed in non-blocking mode.
+ /// </remarks>
+ /// <returns>The number of bytes of available application data.</returns>
+ public virtual int GetAvailableInputBytes()
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use GetAvailableInputBytes() in blocking mode!");
+
+ return ApplicationDataAvailable;
+ }
+
+ /// <summary>Retrieves received application data.</summary>
+ /// <remarks>
+ /// Use <see cref="GetAvailableInputBytes"/> to check how much application data is currently available. This
+ /// method functions similarly to <see cref="Stream.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.
+ /// </remarks>
+ /// <param name="buf">The buffer to hold the application data.</param>
+ /// <param name="off">The start offset in the buffer at which the data is written.</param>
+ /// <param name="len">The maximum number of bytes to read.</param>
+ /// <returns>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.</returns>
+ public virtual int ReadInput(byte[] buf, int off, int len)
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use ReadInput() in blocking mode! Use Stream instead.");
+
+ len = System.Math.Min(len, ApplicationDataAvailable);
+ if (len < 1)
+ return 0;
+
+ m_applicationDataQueue.RemoveData(buf, off, len, 0);
+ return len;
+ }
+
+ /// <summary>Gets the amount of encrypted data available to be sent.</summary>
+ /// <remarks>
+ /// A call to <see cref="ReadOutput(byte[], int, int)"/> is guaranteed to be able to return at least this much
+ /// data. Only allowed in non-blocking mode.
+ /// </remarks>
+ /// <returns>The number of bytes of available encrypted data.</returns>
+ public virtual int GetAvailableOutputBytes()
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use GetAvailableOutputBytes() in blocking mode! Use Stream instead.");
+
+ return m_outputBuffer.Buffer.Available;
+ }
+
+ /// <summary>Retrieves encrypted data to be sent.</summary>
+ /// <remarks>
+ /// Use <see cref="GetAvailableOutputBytes"/> to check how much encrypted data is currently available. This
+ /// method functions similarly to <see cref="Stream.Read(byte[], int, int)"/>, except that it never blocks. If
+ /// no data is available, nothing will be copied and zero will be returned. Only allowed in non-blocking mode.
+ /// </remarks>
+ /// <param name="buffer">The buffer to hold the encrypted data.</param>
+ /// <param name="offset">The start offset in the buffer at which the data is written.</param>
+ /// <param name="length">The maximum number of bytes to read.</param>
+ /// <returns>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.</returns>
+ public virtual int ReadOutput(byte[] buffer, int offset, int length)
+ {
+ if (m_blocking)
+ throw new InvalidOperationException("Cannot use ReadOutput() in blocking mode! Use 'Stream() instead.");
+
+ int bytesToRead = System.Math.Min(GetAvailableOutputBytes(), length);
+ m_outputBuffer.Buffer.RemoveData(buffer, offset, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ protected virtual bool EstablishSession(TlsSession sessionToResume)
+ {
+ this.m_tlsSession = null;
+ this.m_sessionParameters = null;
+ this.m_sessionMasterSecret = null;
+
+ if (null == sessionToResume || !sessionToResume.IsResumable)
+ return false;
+
+ SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
+ if (null == sessionParameters)
+ return false;
+
+ if (!sessionParameters.IsExtendedMasterSecret)
+ {
+ TlsPeer peer = Peer;
+ if (!peer.AllowLegacyResumption() || peer.RequiresExtendedMasterSecret())
+ return false;
+
+ /*
+ * NOTE: For session resumption without extended_master_secret, renegotiation MUST be
+ * disabled (see RFC 7627 5.4). We currently do not implement renegotiation and it is
+ * unlikely we ever would since it was removed in TLS 1.3.
+ */
+ }
+
+ TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(Context.Crypto,
+ sessionParameters.MasterSecret);
+ if (null == sessionMasterSecret)
+ return false;
+
+ this.m_tlsSession = sessionToResume;
+ this.m_sessionParameters = sessionParameters;
+ this.m_sessionMasterSecret = sessionMasterSecret;
+
+ return true;
+ }
+
+ protected virtual void InvalidateSession()
+ {
+ if (m_sessionMasterSecret != null)
+ {
+ m_sessionMasterSecret.Destroy();
+ this.m_sessionMasterSecret = null;
+ }
+
+ if (m_sessionParameters != null)
+ {
+ m_sessionParameters.Clear();
+ this.m_sessionParameters = null;
+ }
+
+ if (m_tlsSession != null)
+ {
+ m_tlsSession.Invalidate();
+ this.m_tlsSession = null;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ProcessFinishedMessage(MemoryStream buf)
+ {
+ TlsContext context = Context;
+ SecurityParameters securityParameters = context.SecurityParameters;
+ bool isServerContext = context.IsServer;
+
+ byte[] verify_data = TlsUtilities.ReadFully(securityParameters.VerifyDataLength, buf);
+
+ AssertEmpty(buf);
+
+ byte[] expected_verify_data = TlsUtilities.CalculateVerifyData(context, m_handshakeHash, !isServerContext);
+
+ /*
+ * Compare both checksums.
+ */
+ if (!Arrays.ConstantTimeAreEqual(expected_verify_data, verify_data))
+ {
+ /*
+ * Wrong checksum in the finished message.
+ */
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+
+ securityParameters.m_peerVerifyData = expected_verify_data;
+
+ if (!m_resumedSession || securityParameters.IsExtendedMasterSecret)
+ {
+ if (null == securityParameters.LocalVerifyData)
+ {
+ securityParameters.m_tlsUnique = expected_verify_data;
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Process13FinishedMessage(MemoryStream buf)
+ {
+ TlsContext context = Context;
+ SecurityParameters securityParameters = context.SecurityParameters;
+ bool isServerContext = context.IsServer;
+
+ byte[] verify_data = TlsUtilities.ReadFully(securityParameters.VerifyDataLength, buf);
+
+ AssertEmpty(buf);
+
+ byte[] expected_verify_data = TlsUtilities.CalculateVerifyData(context, m_handshakeHash, !isServerContext);
+
+ /*
+ * Compare both checksums.
+ */
+ if (!Arrays.ConstantTimeAreEqual(expected_verify_data, verify_data))
+ {
+ /*
+ * Wrong checksum in the finished message.
+ */
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+
+ securityParameters.m_peerVerifyData = expected_verify_data;
+ securityParameters.m_tlsUnique = null;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void RaiseAlertFatal(short alertDescription, string message, Exception cause)
+ {
+ Peer.NotifyAlertRaised(AlertLevel.fatal, alertDescription, message, cause);
+
+ byte[] alert = new byte[]{ (byte)AlertLevel.fatal, (byte)alertDescription };
+
+ try
+ {
+ m_recordStream.WriteRecord(ContentType.alert, alert, 0, 2);
+ }
+ catch (Exception)
+ {
+ // We are already processing an exception, so just ignore this
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void RaiseAlertWarning(short alertDescription, string message)
+ {
+ Peer.NotifyAlertRaised(AlertLevel.warning, alertDescription, message, null);
+
+ byte[] alert = new byte[]{ (byte)AlertLevel.warning, (byte)alertDescription };
+
+ SafeWriteRecord(ContentType.alert, alert, 0, 2);
+ }
+
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13KeyUpdate(MemoryStream buf)
+ {
+ // TODO[tls13] This is interesting enough to notify the TlsPeer for possible logging/vetting
+
+ if (!(m_appDataReady && m_keyUpdateEnabled))
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ short requestUpdate = TlsUtilities.ReadUint8(buf);
+
+ AssertEmpty(buf);
+
+ if (!KeyUpdateRequest.IsValid(requestUpdate))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ bool updateRequested = (KeyUpdateRequest.update_requested == requestUpdate);
+
+ TlsUtilities.Update13TrafficSecretPeer(Context);
+ m_recordStream.NotifyKeyUpdateReceived();
+
+ //this.m_keyUpdatePendingReceive &= updateRequested;
+ this.m_keyUpdatePendingSend |= updateRequested;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendCertificateMessage(Certificate certificate, Stream endPointHash)
+ {
+ TlsContext context = Context;
+ SecurityParameters securityParameters = context.SecurityParameters;
+ if (null != securityParameters.LocalCertificate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (null == certificate)
+ {
+ certificate = Certificate.EmptyChain;
+ }
+
+ if (certificate.IsEmpty && !context.IsServer && securityParameters.NegotiatedVersion.IsSsl)
+ {
+ string message = "SSLv3 client didn't provide credentials";
+ RaiseAlertWarning(AlertDescription.no_certificate, message);
+ }
+ else
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate);
+ certificate.Encode(context, message, endPointHash);
+ message.Send(this);
+ }
+
+ securityParameters.m_localCertificate = certificate;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13CertificateMessage(Certificate certificate)
+ {
+ if (null == certificate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsContext context = Context;
+ SecurityParameters securityParameters = context.SecurityParameters;
+ if (null != securityParameters.LocalCertificate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate);
+ certificate.Encode(context, message, null);
+ message.Send(this);
+
+ securityParameters.m_localCertificate = certificate;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13CertificateVerifyMessage(DigitallySigned certificateVerify)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_verify);
+ certificateVerify.Encode(message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendChangeCipherSpec()
+ {
+ SendChangeCipherSpecMessage();
+ m_recordStream.EnablePendingCipherWrite();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendChangeCipherSpecMessage()
+ {
+ byte[] message = new byte[]{ 1 };
+ SafeWriteRecord(ContentType.change_cipher_spec, message, 0, message.Length);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendFinishedMessage()
+ {
+ TlsContext context = Context;
+ SecurityParameters securityParameters = context.SecurityParameters;
+ bool isServerContext = context.IsServer;
+
+ byte[] verify_data = TlsUtilities.CalculateVerifyData(context, m_handshakeHash, isServerContext);
+
+ securityParameters.m_localVerifyData = verify_data;
+
+ if (!m_resumedSession || securityParameters.IsExtendedMasterSecret)
+ {
+ if (null == securityParameters.PeerVerifyData)
+ {
+ securityParameters.m_tlsUnique = verify_data;
+ }
+ }
+
+ HandshakeMessageOutput.Send(this, HandshakeType.finished, verify_data);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13FinishedMessage()
+ {
+ TlsContext context = Context;
+ SecurityParameters securityParameters = context.SecurityParameters;
+ bool isServerContext = context.IsServer;
+
+ byte[] verify_data = TlsUtilities.CalculateVerifyData(context, m_handshakeHash, isServerContext);
+
+ securityParameters.m_localVerifyData = verify_data;
+ securityParameters.m_tlsUnique = null;
+
+ HandshakeMessageOutput.Send(this, HandshakeType.finished, verify_data);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13KeyUpdate(bool updateRequested)
+ {
+ // TODO[tls13] This is interesting enough to notify the TlsPeer for possible logging/vetting
+
+ if (!(m_appDataReady && m_keyUpdateEnabled))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ short requestUpdate = updateRequested
+ ? KeyUpdateRequest.update_requested
+ : KeyUpdateRequest.update_not_requested;
+
+ HandshakeMessageOutput.Send(this, HandshakeType.key_update, TlsUtilities.EncodeUint8(requestUpdate));
+
+ TlsUtilities.Update13TrafficSecretLocal(Context);
+ m_recordStream.NotifyKeyUpdateSent();
+
+ //this.m_keyUpdatePendingReceive |= updateRequested;
+ this.m_keyUpdatePendingSend &= updateRequested;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendSupplementalDataMessage(IList supplementalData)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.supplemental_data);
+ WriteSupplementalData(message, supplementalData);
+ message.Send(this);
+ }
+
+ public virtual void Close()
+ {
+ HandleClose(true);
+ }
+
+ public virtual void Flush()
+ {
+ }
+
+ internal bool IsApplicationDataReady
+ {
+ get { return m_appDataReady; }
+ }
+
+ public virtual bool IsClosed
+ {
+ get { return m_closed; }
+ }
+
+ public virtual bool IsHandshaking
+ {
+ get
+ {
+ if (m_closed)
+ return false;
+
+ AbstractTlsContext context = ContextAdmin;
+
+ return null != context && !context.IsConnected;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual short ProcessMaxFragmentLengthExtension(IDictionary clientExtensions,
+ IDictionary serverExtensions, short alertDescription)
+ {
+ short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions);
+ if (maxFragmentLength >= 0)
+ {
+ if (!MaxFragmentLength.IsValid(maxFragmentLength)
+ || (!m_resumedSession &&
+ maxFragmentLength != TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions)))
+ {
+ throw new TlsFatalAlert(alertDescription);
+ }
+ }
+ return maxFragmentLength;
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void RefuseRenegotiation()
+ {
+ /*
+ * RFC 5746 4.5 SSLv3 clients [..] SHOULD use a fatal handshake_failure alert.
+ */
+ if (TlsUtilities.IsSsl(Context))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ RaiseAlertWarning(AlertDescription.no_renegotiation, "Renegotiation not supported");
+ }
+
+ /// <summary>Make sure the <see cref="Stream"/> 'buf' is now empty. Fail otherwise.</summary>
+ /// <param name="buf">The <see cref="Stream"/> to check.</param>
+ /// <exception cref="IOException"/>
+ internal static void AssertEmpty(MemoryStream buf)
+ {
+ if (buf.Position < buf.Length)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ internal static byte[] CreateRandomBlock(bool useGmtUnixTime, TlsContext context)
+ {
+ byte[] result = context.NonceGenerator.GenerateNonce(32);
+
+ if (useGmtUnixTime)
+ {
+ TlsUtilities.WriteGmtUnixTime(result, 0);
+ }
+
+ return result;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+ {
+ return TlsUtilities.EncodeOpaque8(renegotiated_connection);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
+ {
+ TlsSecret preMasterSecret = keyExchange.GeneratePreMasterSecret();
+ if (preMasterSecret == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ try
+ {
+ context.SecurityParameters.m_masterSecret = TlsUtilities.CalculateMasterSecret(context,
+ preMasterSecret);
+ }
+ finally
+ {
+ /*
+ * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the
+ * master_secret has been computed.
+ */
+ preMasterSecret.Destroy();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal static IDictionary ReadExtensions(MemoryStream input)
+ {
+ if (input.Position >= input.Length)
+ return null;
+
+ byte[] extBytes = TlsUtilities.ReadOpaque16(input);
+
+ AssertEmpty(input);
+
+ return ReadExtensionsData(extBytes);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static IDictionary ReadExtensionsData(byte[] extBytes)
+ {
+ // Int32 -> byte[]
+ IDictionary extensions = Platform.CreateHashtable();
+
+ if (extBytes.Length > 0)
+ {
+ MemoryStream buf = new MemoryStream(extBytes, false);
+
+ do
+ {
+ 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.
+ */
+ Int32 key = extension_type;
+ if (extensions.Contains(key))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+ "Repeated extension: " + ExtensionType.GetText(extension_type));
+
+ extensions.Add(key, extension_data);
+ }
+ while (buf.Position < buf.Length);
+ }
+
+ return extensions;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static IDictionary ReadExtensionsData13(int handshakeType, byte[] extBytes)
+ {
+ // Int32 -> byte[]
+ IDictionary extensions = Platform.CreateHashtable();
+
+ if (extBytes.Length > 0)
+ {
+ MemoryStream buf = new MemoryStream(extBytes, false);
+
+ do
+ {
+ int extension_type = TlsUtilities.ReadUint16(buf);
+
+ if (!TlsUtilities.IsPermittedExtensionType13(handshakeType, extension_type))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+ "Invalid extension: " + ExtensionType.GetText(extension_type));
+ }
+
+ byte[] extension_data = TlsUtilities.ReadOpaque16(buf);
+
+ /*
+ * RFC 3546 2.3 There MUST NOT be more than one extension of the same type.
+ */
+ Int32 key = extension_type;
+ if (extensions.Contains(key))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+ "Repeated extension: " + ExtensionType.GetText(extension_type));
+
+ extensions.Add(key, extension_data);
+ }
+ while (buf.Position < buf.Length);
+ }
+
+ return extensions;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static IDictionary ReadExtensionsDataClientHello(byte[] extBytes)
+ {
+ /*
+ * TODO[tls13] We are currently allowing any extensions to appear in ClientHello. It is
+ * somewhat complicated to restrict what can appear based on the specific set of versions
+ * the client is offering, and anyway could be fragile since clients may take a
+ * "kitchen sink" approach to adding extensions independently of the offered versions.
+ */
+
+ // Int32 -> byte[]
+ IDictionary extensions = Platform.CreateHashtable();
+
+ if (extBytes.Length > 0)
+ {
+ MemoryStream buf = new MemoryStream(extBytes, false);
+
+ int extension_type;
+ bool pre_shared_key_found = false;
+
+ do
+ {
+ 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.
+ */
+ Int32 key = extension_type;
+ if (extensions.Contains(key))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+ "Repeated extension: " + ExtensionType.GetText(extension_type));
+
+ extensions.Add(key, extension_data);
+
+ pre_shared_key_found |= (ExtensionType.pre_shared_key == extension_type);
+ }
+ while (buf.Position < buf.Length);
+
+ if (pre_shared_key_found && (ExtensionType.pre_shared_key != extension_type))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+ "'pre_shared_key' MUST be last in ClientHello");
+ }
+
+ return extensions;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static IList ReadSupplementalDataMessage(MemoryStream input)
+ {
+ byte[] supp_data = TlsUtilities.ReadOpaque24(input, 1);
+
+ 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;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void WriteExtensions(Stream output, IDictionary extensions)
+ {
+ if (null == extensions || extensions.Count < 1)
+ return;
+
+ byte[] extBytes = WriteExtensionsData(extensions);
+
+ TlsUtilities.WriteOpaque16(extBytes, output);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] WriteExtensionsData(IDictionary extensions)
+ {
+ MemoryStream buf = new MemoryStream();
+ WriteExtensionsData(extensions, buf);
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void WriteExtensionsData(IDictionary extensions, MemoryStream buf)
+ {
+ /*
+ * NOTE: There are reports of servers that don't accept a zero-length extension as the last
+ * one, so we write out any zero-length ones first as a best-effort workaround.
+ */
+ WriteSelectedExtensions(buf, extensions, true);
+ WriteSelectedExtensions(buf, extensions, false);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void WriteSelectedExtensions(Stream output, IDictionary extensions, bool selectEmpty)
+ {
+ foreach (Int32 key in extensions.Keys)
+ {
+ int extension_type = key;
+ byte[] extension_data = (byte[])extensions[key];
+
+ if (selectEmpty == (extension_data.Length == 0))
+ {
+ TlsUtilities.CheckUint16(extension_type);
+ TlsUtilities.WriteUint16(extension_type, output);
+ TlsUtilities.WriteOpaque16(extension_data, output);
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ 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);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsPskIdentity.cs b/crypto/src/tls/TlsPskIdentity.cs
new file mode 100644
index 000000000..ae78872c3
--- /dev/null
+++ b/crypto/src/tls/TlsPskIdentity.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Processor interface for a PSK identity.</summary>
+ public interface TlsPskIdentity
+ {
+ void SkipIdentityHint();
+
+ void NotifyIdentityHint(byte[] psk_identity_hint);
+
+ byte[] GetPskIdentity();
+
+ byte[] GetPsk();
+ }
+}
diff --git a/crypto/src/tls/TlsPskIdentityManager.cs b/crypto/src/tls/TlsPskIdentityManager.cs
new file mode 100644
index 000000000..5bf10bccd
--- /dev/null
+++ b/crypto/src/tls/TlsPskIdentityManager.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for an object that can process a PSK identity.</summary>
+ public interface TlsPskIdentityManager
+ {
+ byte[] GetHint();
+
+ byte[] GetPsk(byte[] identity);
+ }
+}
diff --git a/crypto/src/tls/TlsPskKeyExchange.cs b/crypto/src/tls/TlsPskKeyExchange.cs
new file mode 100644
index 000000000..1055fdc53
--- /dev/null
+++ b/crypto/src/tls/TlsPskKeyExchange.cs
@@ -0,0 +1,305 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS PSK key exchange (RFC 4279).</summary>
+ public class TlsPskKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsPskIdentity m_pskIdentity;
+ protected TlsPskIdentityManager m_pskIdentityManager;
+ protected TlsDHGroupVerifier m_dhGroupVerifier;
+
+ protected byte[] m_psk_identity_hint = null;
+ protected byte[] m_psk = null;
+
+ protected TlsDHConfig m_dhConfig;
+ protected TlsECConfig m_ecConfig;
+ protected TlsAgreement m_agreement;
+
+ protected TlsCredentialedDecryptor m_serverCredentials = null;
+ protected TlsCertificate m_serverCertificate;
+ protected TlsSecret m_preMasterSecret;
+
+ public TlsPskKeyExchange(int keyExchange, TlsPskIdentity pskIdentity, TlsDHGroupVerifier dhGroupVerifier)
+ : this(keyExchange, pskIdentity, null, dhGroupVerifier, null, null)
+ {
+ }
+
+ public TlsPskKeyExchange(int keyExchange, TlsPskIdentityManager pskIdentityManager,
+ TlsDHConfig dhConfig, TlsECConfig ecConfig)
+ : this(keyExchange, null, pskIdentityManager, null, dhConfig, ecConfig)
+ {
+ }
+
+ private TlsPskKeyExchange(int keyExchange, TlsPskIdentity pskIdentity, TlsPskIdentityManager pskIdentityManager,
+ TlsDHGroupVerifier dhGroupVerifier, TlsDHConfig dhConfig, TlsECConfig ecConfig)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_pskIdentity = pskIdentity;
+ this.m_pskIdentityManager = pskIdentityManager;
+ this.m_dhGroupVerifier = dhGroupVerifier;
+ this.m_dhConfig = dhConfig;
+ this.m_ecConfig = ecConfig;
+ }
+
+ public override void SkipServerCredentials()
+ {
+ if (m_keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ if (m_keyExchange != KeyExchangeAlgorithm.RSA_PSK)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_serverCredentials = TlsUtilities.RequireDecryptorCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ if (m_keyExchange != KeyExchangeAlgorithm.RSA_PSK)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ this.m_serverCertificate = serverCertificate.GetCertificateAt(0).CheckUsageInRole(ConnectionEnd.server,
+ TlsCertificateRole.RsaEncryption);
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ this.m_psk_identity_hint = m_pskIdentityManager.GetHint();
+
+ if (this.m_psk_identity_hint == null && !RequiresServerKeyExchange)
+ return null;
+
+ MemoryStream buf = new MemoryStream();
+
+ if (this.m_psk_identity_hint == null)
+ {
+ TlsUtilities.WriteOpaque16(TlsUtilities.EmptyBytes, buf);
+ }
+ else
+ {
+ TlsUtilities.WriteOpaque16(this.m_psk_identity_hint, buf);
+ }
+
+ if (this.m_keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ if (this.m_dhConfig == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsDHUtilities.WriteDHConfig(m_dhConfig, buf);
+
+ this.m_agreement = m_context.Crypto.CreateDHDomain(m_dhConfig).CreateDH();
+
+ GenerateEphemeralDH(buf);
+ }
+ else if (this.m_keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ if (this.m_ecConfig == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsEccUtilities.WriteECConfig(m_ecConfig, buf);
+
+ this.m_agreement = m_context.Crypto.CreateECDomain(m_ecConfig).CreateECDH();
+
+ GenerateEphemeralECDH(buf);
+ }
+
+ return buf.ToArray();
+ }
+
+ public override bool RequiresServerKeyExchange
+ {
+ get
+ {
+ switch (m_keyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ this.m_psk_identity_hint = TlsUtilities.ReadOpaque16(input);
+
+ if (this.m_keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ this.m_dhConfig = TlsDHUtilities.ReceiveDHConfig(m_context, m_dhGroupVerifier, input);
+
+ byte[] y = TlsUtilities.ReadOpaque16(input, 1);
+
+ this.m_agreement = m_context.Crypto.CreateDHDomain(m_dhConfig).CreateDH();
+
+ ProcessEphemeralDH(y);
+ }
+ else if (this.m_keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ this.m_ecConfig = TlsEccUtilities.ReceiveECDHConfig(m_context, input);
+
+ byte[] point = TlsUtilities.ReadOpaque8(input, 1);
+
+ this.m_agreement = m_context.Crypto.CreateECDomain(m_ecConfig).CreateECDH();
+
+ ProcessEphemeralECDH(point);
+ }
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ if (m_psk_identity_hint == null)
+ {
+ m_pskIdentity.SkipIdentityHint();
+ }
+ else
+ {
+ m_pskIdentity.NotifyIdentityHint(m_psk_identity_hint);
+ }
+
+ byte[] psk_identity = m_pskIdentity.GetPskIdentity();
+ if (psk_identity == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_psk = m_pskIdentity.GetPsk();
+ if (m_psk == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsUtilities.WriteOpaque16(psk_identity, output);
+
+ m_context.SecurityParameters.m_pskIdentity = Arrays.Clone(psk_identity);
+
+ if (this.m_keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ GenerateEphemeralDH(output);
+ }
+ else if (this.m_keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ GenerateEphemeralECDH(output);
+ }
+ else if (this.m_keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ this.m_preMasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(m_context,
+ m_serverCertificate, output);
+ }
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ byte[] psk_identity = TlsUtilities.ReadOpaque16(input);
+
+ this.m_psk = m_pskIdentityManager.GetPsk(psk_identity);
+ if (m_psk == null)
+ throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
+
+ m_context.SecurityParameters.m_pskIdentity = psk_identity;
+
+ if (this.m_keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ byte[] y = TlsUtilities.ReadOpaque16(input, 1);
+
+ ProcessEphemeralDH(y);
+ }
+ else if (this.m_keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ byte[] point = TlsUtilities.ReadOpaque8(input, 1);
+
+ ProcessEphemeralECDH(point);
+ }
+ else if (this.m_keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ byte[] encryptedPreMasterSecret = TlsUtilities.ReadEncryptedPms(m_context, input);
+
+ this.m_preMasterSecret = m_serverCredentials.Decrypt(new TlsCryptoParameters(m_context),
+ encryptedPreMasterSecret);
+ }
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ byte[] other_secret = GenerateOtherSecret(m_psk.Length);
+
+ MemoryStream buf = new MemoryStream(4 + other_secret.Length + m_psk.Length);
+ TlsUtilities.WriteOpaque16(other_secret, buf);
+ TlsUtilities.WriteOpaque16(m_psk, buf);
+
+ Array.Clear(m_psk, 0, m_psk.Length);
+ this.m_psk = null;
+
+ return m_context.Crypto.CreateSecret(buf.ToArray());
+ }
+
+ protected virtual void GenerateEphemeralDH(Stream output)
+ {
+ byte[] y = m_agreement.GenerateEphemeral();
+ TlsUtilities.WriteOpaque16(y, output);
+ }
+
+ protected virtual void GenerateEphemeralECDH(Stream output)
+ {
+ byte[] point = m_agreement.GenerateEphemeral();
+ TlsUtilities.WriteOpaque8(point, output);
+ }
+
+ protected virtual byte[] GenerateOtherSecret(int pskLength)
+ {
+ if (this.m_keyExchange == KeyExchangeAlgorithm.PSK)
+ return new byte[pskLength];
+
+ if (this.m_keyExchange == KeyExchangeAlgorithm.DHE_PSK ||
+ this.m_keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ if (m_agreement != null)
+ return m_agreement.CalculateSecret().Extract();
+ }
+
+ if (this.m_keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ if (m_preMasterSecret != null)
+ return this.m_preMasterSecret.Extract();
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected virtual void ProcessEphemeralDH(byte[] y)
+ {
+ this.m_agreement.ReceivePeerValue(y);
+ }
+
+ protected virtual void ProcessEphemeralECDH(byte[] point)
+ {
+ TlsEccUtilities.CheckPointEncoding(m_ecConfig.NamedGroup, point);
+
+ this.m_agreement.ReceivePeerValue(point);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsRsaKeyExchange.cs b/crypto/src/tls/TlsRsaKeyExchange.cs
new file mode 100644
index 000000000..5184ca8fa
--- /dev/null
+++ b/crypto/src/tls/TlsRsaKeyExchange.cs
@@ -0,0 +1,80 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS RSA key exchange.</summary>
+ public class TlsRsaKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.RSA:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsCredentialedDecryptor m_serverCredentials = null;
+ protected TlsCertificate m_serverCertificate;
+ protected TlsSecret m_preMasterSecret;
+
+ public TlsRsaKeyExchange(int keyExchange)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ }
+
+ public override void SkipServerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ this.m_serverCredentials = TlsUtilities.RequireDecryptorCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ this.m_serverCertificate = serverCertificate.GetCertificateAt(0).CheckUsageInRole(ConnectionEnd.server,
+ TlsCertificateRole.RsaEncryption);
+ }
+
+ public override short[] GetClientCertificateTypes()
+ {
+ return new short[]{ ClientCertificateType.rsa_sign, ClientCertificateType.dss_sign,
+ ClientCertificateType.ecdsa_sign };
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ TlsUtilities.RequireSignerCredentials(clientCredentials);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ this.m_preMasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(m_context, m_serverCertificate,
+ output);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ byte[] encryptedPreMasterSecret = TlsUtilities.ReadEncryptedPms(m_context, input);
+
+ this.m_preMasterSecret = m_serverCredentials.Decrypt(new TlsCryptoParameters(m_context),
+ encryptedPreMasterSecret);
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ TlsSecret tmp = this.m_preMasterSecret;
+ this.m_preMasterSecret = null;
+ return tmp;
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsRsaUtilities.cs b/crypto/src/tls/TlsRsaUtilities.cs
new file mode 100644
index 000000000..d520d3ea2
--- /dev/null
+++ b/crypto/src/tls/TlsRsaUtilities.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RSA Utility methods.</summary>
+ public abstract class TlsRsaUtilities
+ {
+ /// <summary>Generate a pre_master_secret and send it encrypted to the server.</summary>
+ /// <exception cref="IOException"/>
+ public static TlsSecret GenerateEncryptedPreMasterSecret(TlsContext context, TlsCertificate certificate,
+ Stream output)
+ {
+ TlsSecret preMasterSecret = context.Crypto.GenerateRsaPreMasterSecret(context.RsaPreMasterSecretVersion);
+
+ byte[] encryptedPreMasterSecret = preMasterSecret.Encrypt(certificate);
+ TlsUtilities.WriteEncryptedPms(context, encryptedPreMasterSecret, output);
+
+ return preMasterSecret;
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsServer.cs b/crypto/src/tls/TlsServer.cs
new file mode 100644
index 000000000..783c8c14d
--- /dev/null
+++ b/crypto/src/tls/TlsServer.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Interface describing a TLS server endpoint.</summary>
+ public interface TlsServer
+ : TlsPeer
+ {
+ void Init(TlsServerContext context);
+
+ /// <summary>Return the specified session, if available.</summary>
+ /// <remarks>
+ /// Note that the peer's certificate chain for the session (if any) may need to be periodically revalidated.
+ /// </remarks>
+ /// <param name="sessionID">the ID of the session to resume.</param>
+ /// <returns>A <see cref="TlsSession"/> with the specified session ID, or null.</returns>
+ /// <seealso cref="SessionParameters.PeerCertificate"/>
+ TlsSession GetSessionToResume(byte[] sessionID);
+
+ byte[] GetNewSessionID();
+
+ void NotifySession(TlsSession session);
+
+ /// <exception cref="IOException"/>
+ void NotifyClientVersion(ProtocolVersion clientVersion);
+
+ /// <exception cref="IOException"/>
+ void NotifyFallback(bool isFallback);
+
+ /// <exception cref="IOException"/>
+ void NotifyOfferedCipherSuites(int[] offeredCipherSuites);
+
+ /// <param name="clientExtensions">(Int32 -> byte[])</param>
+ /// <exception cref="IOException"/>
+ void ProcessClientExtensions(IDictionary clientExtensions);
+
+ /// <exception cref="IOException"/>
+ ProtocolVersion GetServerVersion();
+
+ /// <exception cref="IOException"/>
+ int[] GetSupportedGroups();
+
+ /// <exception cref="IOException"/>
+ int GetSelectedCipherSuite();
+
+ /// <returns>(Int32 -> byte[])</returns>
+ /// <exception cref="IOException"/>
+ IDictionary GetServerExtensions();
+
+ /// <param name="serverExtensions">(Int32 -> byte[])</param>
+ /// <exception cref="IOException"/>
+ void GetServerExtensionsForConnection(IDictionary serverExtensions);
+
+ /// <returns>(SupplementalDataEntry)</returns>
+ /// <exception cref="IOException"/>
+ IList GetServerSupplementalData();
+
+ /// <summary>Return server credentials to use.</summary>
+ /// <remarks>
+ /// The returned value may be null, or else it MUST implement <em>exactly one</em> of
+ /// <see cref="TlsCredentialedAgreement"/>, <see cref="TlsCredentialedDecryptor"/>, or
+ /// <see cref = "TlsCredentialedSigner"/>, depending on the key exchange that was negotiated.
+ /// </remarks>
+ /// <returns>a <see cref="TlsCredentials"/> object or null for anonymous key exchanges.</returns>
+ /// <exception cref="IOException"/>
+ 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"/>
+ CertificateStatus GetCertificateStatus();
+
+ /// <exception cref="IOException"/>
+ CertificateRequest GetCertificateRequest();
+
+ /// <exception cref="IOException"/>
+ TlsPskIdentityManager GetPskIdentityManager();
+
+ /// <exception cref="IOException"/>
+ TlsSrpLoginParameters GetSrpLoginParameters();
+
+ /// <exception cref="IOException"/>
+ TlsDHConfig GetDHConfig();
+
+ /// <exception cref="IOException"/>
+ TlsECConfig GetECDHConfig();
+
+ /// <param name="clientSupplementalData">(SupplementalDataEntry)</param>
+ /// <exception cref="IOException"/>
+ void ProcessClientSupplementalData(IList clientSupplementalData);
+
+ /// <summary>Called by the protocol handler to report the client certificate, only if
+ /// <see cref="GetCertificateRequest"/> 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"/>
+ 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 ticket.</returns>
+ /// <exception cref="IOException"/>
+ NewSessionTicket GetNewSessionTicket();
+ }
+}
diff --git a/crypto/src/tls/TlsServerCertificate.cs b/crypto/src/tls/TlsServerCertificate.cs
new file mode 100644
index 000000000..bea896af7
--- /dev/null
+++ b/crypto/src/tls/TlsServerCertificate.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Server certificate carrier interface.</summary>
+ public interface TlsServerCertificate
+ {
+ Certificate Certificate { get; }
+
+ CertificateStatus CertificateStatus { get; }
+ }
+}
diff --git a/crypto/src/tls/TlsServerCertificateImpl.cs b/crypto/src/tls/TlsServerCertificateImpl.cs
new file mode 100644
index 000000000..db2d0911f
--- /dev/null
+++ b/crypto/src/tls/TlsServerCertificateImpl.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal sealed class TlsServerCertificateImpl
+ : TlsServerCertificate
+ {
+ private readonly Certificate m_certificate;
+ private readonly CertificateStatus m_certificateStatus;
+
+ internal TlsServerCertificateImpl(Certificate certificate, CertificateStatus certificateStatus)
+ {
+ this.m_certificate = certificate;
+ this.m_certificateStatus = certificateStatus;
+ }
+
+ public Certificate Certificate
+ {
+ get { return m_certificate; }
+ }
+
+ public CertificateStatus CertificateStatus
+ {
+ get { return m_certificateStatus; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsServerContext.cs b/crypto/src/tls/TlsServerContext.cs
new file mode 100644
index 000000000..85a075154
--- /dev/null
+++ b/crypto/src/tls/TlsServerContext.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Marker interface to distinguish a TLS server context.</summary>
+ public interface TlsServerContext
+ : TlsContext
+ {
+ }
+}
diff --git a/crypto/src/tls/TlsServerContextImpl.cs b/crypto/src/tls/TlsServerContextImpl.cs
new file mode 100644
index 000000000..0c719bce5
--- /dev/null
+++ b/crypto/src/tls/TlsServerContextImpl.cs
@@ -0,0 +1,20 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class TlsServerContextImpl
+ : AbstractTlsContext, TlsServerContext
+ {
+ internal TlsServerContextImpl(TlsCrypto crypto)
+ : base(crypto, ConnectionEnd.server)
+ {
+ }
+
+ public override bool IsServer
+ {
+ get { return true; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsServerProtocol.cs b/crypto/src/tls/TlsServerProtocol.cs
new file mode 100644
index 000000000..a7e0e0120
--- /dev/null
+++ b/crypto/src/tls/TlsServerProtocol.cs
@@ -0,0 +1,1471 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsServerProtocol
+ : TlsProtocol
+ {
+ protected TlsServer m_tlsServer = null;
+ internal TlsServerContextImpl m_tlsServerContext = null;
+
+ protected int[] m_offeredCipherSuites = null;
+ protected TlsKeyExchange m_keyExchange = null;
+ protected CertificateRequest m_certificateRequest = null;
+
+ /// <summary>Constructor for non-blocking mode.</summary>
+ /// <remarks>
+ /// When data is received, use <see cref="TlsProtocol.OfferInput(byte[])"/> to provide the received ciphertext,
+ /// then use <see cref="TlsProtocol.ReadInput(byte[],int,int)"/> to read the corresponding cleartext.<br/><br/>
+ /// Similarly, when data needs to be sent, use <see cref="TlsProtocol.WriteApplicationData(byte[],int,int)"/>
+ /// to provide the cleartext, then use <see cref="TlsProtocol.ReadOutput(byte[],int,int)"/> to get the
+ /// corresponding ciphertext.
+ /// </remarks>
+ public TlsServerProtocol()
+ : base()
+ {
+ }
+
+ /// <summary>Constructor for blocking mode.</summary>
+ /// <param name="stream">The <see cref="Stream"/> of data to/from the server.</param>
+ public TlsServerProtocol(Stream stream)
+ : base(stream)
+ {
+ }
+
+ /// <summary>Constructor for blocking mode.</summary>
+ /// <param name="input">The <see cref="Stream"/> of data from the server.</param>
+ /// <param name="output">The <see cref="Stream"/> of data to the server.</param>
+ public TlsServerProtocol(Stream input, Stream output)
+ : base(input, output)
+ {
+ }
+
+ /// <summary>Receives a TLS handshake in the role of server.</summary>
+ /// <remarks>
+ /// In blocking mode, this will not return until the handshake is complete. In non-blocking mode, use
+ /// <see cref="TlsPeer.NotifyHandshakeComplete"/> to receive a callback when the handshake is complete.
+ /// </remarks>
+ /// <param name="tlsServer">The <see cref="TlsServer"/> to use for the handshake.</param>
+ /// <exception cref="IOException">If in blocking mode and handshake was not successful.</exception>
+ public void Accept(TlsServer tlsServer)
+ {
+ if (tlsServer == null)
+ throw new ArgumentNullException("tlsServer");
+ if (m_tlsServer != null)
+ throw new InvalidOperationException("'Accept' can only be called once");
+
+ this.m_tlsServer = tlsServer;
+ this.m_tlsServerContext = new TlsServerContextImpl(tlsServer.Crypto);
+
+ tlsServer.Init(m_tlsServerContext);
+ tlsServer.NotifyCloseHandle(this);
+
+ BeginHandshake();
+
+ if (m_blocking)
+ {
+ BlockForHandshake();
+ }
+ }
+
+ protected override void CleanupHandshake()
+ {
+ base.CleanupHandshake();
+
+ this.m_offeredCipherSuites = null;
+ this.m_keyExchange = null;
+ this.m_certificateRequest = null;
+ }
+
+ protected virtual bool ExpectCertificateVerifyMessage()
+ {
+ if (null == m_certificateRequest)
+ return false;
+
+ Certificate clientCertificate = m_tlsServerContext.SecurityParameters.PeerCertificate;
+
+ return null != clientCertificate && !clientCertificate.IsEmpty
+ && (null == m_keyExchange || m_keyExchange.RequiresCertificateVerify);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual ServerHello Generate13HelloRetryRequest(ClientHello clientHello)
+ {
+ // TODO[tls13] In future there might be other reasons for a HelloRetryRequest.
+ if (m_retryGroup < 0)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
+ ProtocolVersion serverVersion = securityParameters.NegotiatedVersion;
+
+ IDictionary serverHelloExtensions = Platform.CreateHashtable();
+ TlsExtensionsUtilities.AddSupportedVersionsExtensionServer(serverHelloExtensions, serverVersion);
+ if (m_retryGroup >= 0)
+ {
+ TlsExtensionsUtilities.AddKeyShareHelloRetryRequest(serverHelloExtensions, m_retryGroup);
+ }
+ if (null != m_retryCookie)
+ {
+ TlsExtensionsUtilities.AddCookieExtension(serverHelloExtensions, m_retryCookie);
+ }
+
+ TlsUtilities.CheckExtensionData13(serverHelloExtensions, HandshakeType.hello_retry_request,
+ AlertDescription.internal_error);
+
+ return new ServerHello(clientHello.SessionID, securityParameters.CipherSuite, serverHelloExtensions);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual ServerHello Generate13ServerHello(ClientHello clientHello, bool afterHelloRetryRequest)
+ {
+ SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
+
+
+ byte[] legacy_session_id = clientHello.SessionID;
+
+ IDictionary clientHelloExtensions = clientHello.Extensions;
+ if (null == clientHelloExtensions)
+ throw new TlsFatalAlert(AlertDescription.missing_extension);
+
+
+ ProtocolVersion serverVersion = securityParameters.NegotiatedVersion;
+ TlsCrypto crypto = m_tlsServerContext.Crypto;
+
+ IList clientShares = TlsExtensionsUtilities.GetKeyShareClientHello(clientHelloExtensions);
+ KeyShareEntry clientShare = null;
+
+ if (afterHelloRetryRequest)
+ {
+ if (m_retryGroup < 0)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ /*
+ * TODO[tls13] Confirm fields in the ClientHello haven't changed
+ *
+ * RFC 8446 4.1.2 [..] when the server has responded to its ClientHello with a
+ * HelloRetryRequest [..] the client MUST send the same ClientHello without
+ * modification, except as follows: [key_share, early_data, cookie, pre_shared_key,
+ * padding].
+ */
+
+ byte[] cookie = TlsExtensionsUtilities.GetCookieExtension(clientHelloExtensions);
+ if (!Arrays.AreEqual(m_retryCookie, cookie))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.m_retryCookie = null;
+
+ clientShare = TlsUtilities.SelectKeyShare(clientShares, m_retryGroup);
+ if (null == clientShare)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ else
+ {
+ this.m_clientExtensions = clientHelloExtensions;
+
+ securityParameters.m_secureRenegotiation = false;
+
+ // NOTE: Validates the padding extension data, if present
+ TlsExtensionsUtilities.GetPaddingExtension(clientHelloExtensions);
+
+ securityParameters.m_clientServerNames = TlsExtensionsUtilities
+ .GetServerNameExtensionClient(clientHelloExtensions);
+
+ TlsUtilities.EstablishClientSigAlgs(securityParameters, clientHelloExtensions);
+
+ /*
+ * RFC 8446 4.2.3. If a server is authenticating via a certificate and the client has
+ * not sent a "signature_algorithms" extension, then the server MUST abort the handshake
+ * with a "missing_extension" alert.
+ */
+ // TODO[tls13] Revisit this check if we add support for PSK-only key exchange.
+ if (null == securityParameters.ClientSigAlgs)
+ throw new TlsFatalAlert(AlertDescription.missing_extension);
+
+ m_tlsServer.ProcessClientExtensions(clientHelloExtensions);
+
+ /*
+ * NOTE: Currently no server support for session resumption
+ *
+ * If adding support, ensure securityParameters.tlsUnique is set to the localVerifyData, but
+ * ONLY when extended_master_secret has been negotiated (otherwise NULL).
+ */
+ {
+ // TODO[tls13] Resumption/PSK
+
+ this.m_tlsSession = TlsUtilities.ImportSession(TlsUtilities.EmptyBytes, null);
+ this.m_sessionParameters = null;
+ this.m_sessionMasterSecret = null;
+ }
+
+ securityParameters.m_sessionID = m_tlsSession.SessionID;
+
+ m_tlsServer.NotifySession(m_tlsSession);
+
+ TlsUtilities.NegotiatedVersionTlsServer(m_tlsServerContext);
+
+ {
+ securityParameters.m_serverRandom = CreateRandomBlock(false, m_tlsServerContext);
+
+ if (!serverVersion.Equals(ProtocolVersion.GetLatestTls(m_tlsServer.GetProtocolVersions())))
+ {
+ TlsUtilities.WriteDowngradeMarker(serverVersion, securityParameters.ServerRandom);
+ }
+ }
+
+ {
+ int cipherSuite = m_tlsServer.GetSelectedCipherSuite();
+
+ if (!TlsUtilities.IsValidCipherSuiteSelection(m_offeredCipherSuites, cipherSuite) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, serverVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ }
+
+ int[] clientSupportedGroups = securityParameters.ClientSupportedGroups;
+ int[] serverSupportedGroups = securityParameters.ServerSupportedGroups;
+
+ clientShare = TlsUtilities.SelectKeyShare(crypto, serverVersion, clientShares, clientSupportedGroups,
+ serverSupportedGroups);
+
+ if (null == clientShare)
+ {
+ this.m_retryGroup = TlsUtilities.SelectKeyShareGroup(crypto, serverVersion, clientSupportedGroups,
+ serverSupportedGroups);
+ if (m_retryGroup < 0)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ this.m_retryCookie = m_tlsServerContext.NonceGenerator.GenerateNonce(16);
+
+ return Generate13HelloRetryRequest(clientHello);
+ }
+
+ if (clientShare.NamedGroup != serverSupportedGroups[0])
+ {
+ /*
+ * TODO[tls13] RFC 8446 4.2.7. As of TLS 1.3, servers are permitted to send the
+ * "supported_groups" extension to the client. Clients MUST NOT act upon any
+ * information found in "supported_groups" prior to successful completion of the
+ * handshake but MAY use the information learned from a successfully completed
+ * handshake to change what groups they use in their "key_share" extension in
+ * subsequent connections. If the server has a group it prefers to the ones in the
+ * "key_share" extension but is still willing to accept the ClientHello, it SHOULD
+ * send "supported_groups" to update the client's view of its preferences; this
+ * extension SHOULD contain all groups the server supports, regardless of whether
+ * they are currently supported by the client.
+ */
+ }
+ }
+
+
+ IDictionary serverHelloExtensions = Platform.CreateHashtable();
+ IDictionary serverEncryptedExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ m_tlsServer.GetServerExtensions());
+
+ m_tlsServer.GetServerExtensionsForConnection(serverEncryptedExtensions);
+
+ ProtocolVersion serverLegacyVersion = ProtocolVersion.TLSv12;
+ TlsExtensionsUtilities.AddSupportedVersionsExtensionServer(serverHelloExtensions, serverVersion);
+
+ /*
+ * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
+ * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
+ * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
+ */
+ securityParameters.m_extendedMasterSecret = true;
+
+ /*
+ * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
+ * contents of this extension are irrelevant, and only the values in the new handshake
+ * messages are considered.
+ */
+ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
+ serverEncryptedExtensions);
+ securityParameters.m_applicationProtocolSet = true;
+
+ if (serverEncryptedExtensions.Count > 0)
+ {
+ securityParameters.m_maxFragmentLength = ProcessMaxFragmentLengthExtension(clientHelloExtensions,
+ serverEncryptedExtensions, AlertDescription.internal_error);
+ }
+
+ securityParameters.m_encryptThenMac = false;
+ securityParameters.m_truncatedHmac = false;
+
+ /*
+ * TODO[tls13] RFC 8446 4.4.2.1. OCSP Status and SCT Extensions.
+ *
+ * OCSP information is carried in an extension for a CertificateEntry.
+ */
+ securityParameters.m_statusRequestVersion = clientHelloExtensions.Contains(ExtensionType.status_request)
+ ? 1 : 0;
+
+ this.m_expectSessionTicket = false;
+
+ {
+ int namedGroup = clientShare.NamedGroup;
+
+ TlsAgreement agreement;
+ if (NamedGroup.RefersToASpecificCurve(namedGroup))
+ {
+ agreement = crypto.CreateECDomain(new TlsECConfig(namedGroup)).CreateECDH();
+ }
+ else if (NamedGroup.RefersToASpecificFiniteField(namedGroup))
+ {
+ agreement = crypto.CreateDHDomain(new TlsDHConfig(namedGroup, true)).CreateDH();
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ byte[] key_exchange = agreement.GenerateEphemeral();
+ KeyShareEntry serverShare = new KeyShareEntry(namedGroup, key_exchange);
+ TlsExtensionsUtilities.AddKeyShareServerHello(serverHelloExtensions, serverShare);
+
+ agreement.ReceivePeerValue(clientShare.KeyExchange);
+ securityParameters.m_sharedSecret = agreement.CalculateSecret();
+ TlsUtilities.Establish13PhaseSecrets(m_tlsServerContext);
+ }
+
+ this.m_serverExtensions = serverEncryptedExtensions;
+
+ ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
+
+ TlsUtilities.CheckExtensionData13(serverHelloExtensions, HandshakeType.server_hello,
+ AlertDescription.internal_error);
+
+ return new ServerHello(serverLegacyVersion, securityParameters.ServerRandom, legacy_session_id,
+ securityParameters.CipherSuite, serverHelloExtensions);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual ServerHello GenerateServerHello(ClientHello clientHello)
+ {
+ ProtocolVersion clientLegacyVersion = clientHello.Version;
+ if (!clientLegacyVersion.IsTls)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.m_offeredCipherSuites = clientHello.CipherSuites;
+
+
+
+ SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
+
+ m_tlsServerContext.SetClientSupportedVersions(
+ TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(clientHello.Extensions));
+
+ ProtocolVersion clientVersion = clientLegacyVersion;
+ if (null == m_tlsServerContext.ClientSupportedVersions)
+ {
+ if (clientVersion.IsLaterVersionOf(ProtocolVersion.TLSv12))
+ {
+ clientVersion = ProtocolVersion.TLSv12;
+ }
+
+ m_tlsServerContext.SetClientSupportedVersions(clientVersion.DownTo(ProtocolVersion.SSLv3));
+ }
+ else
+ {
+ clientVersion = ProtocolVersion.GetLatestTls(m_tlsServerContext.ClientSupportedVersions);
+ }
+
+ // Set the legacy_record_version to use for early alerts
+ m_recordStream.SetWriteVersion(clientVersion);
+
+ if (!ProtocolVersion.SERVER_EARLIEST_SUPPORTED_TLS.IsEqualOrEarlierVersionOf(clientVersion))
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+
+ // NOT renegotiating
+ {
+ m_tlsServerContext.SetClientVersion(clientVersion);
+ }
+
+ m_tlsServer.NotifyClientVersion(m_tlsServerContext.ClientVersion);
+
+ securityParameters.m_clientRandom = clientHello.Random;
+
+ m_tlsServer.NotifyFallback(Arrays.Contains(m_offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
+
+ m_tlsServer.NotifyOfferedCipherSuites(m_offeredCipherSuites);
+
+ // TODO[tls13] Negotiate cipher suite first?
+
+ ProtocolVersion serverVersion;
+
+ // NOT renegotiating
+ {
+ serverVersion = m_tlsServer.GetServerVersion();
+ if (!ProtocolVersion.Contains(m_tlsServerContext.ClientSupportedVersions, serverVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ securityParameters.m_negotiatedVersion = serverVersion;
+ }
+
+ securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
+ clientHello.Extensions);
+ securityParameters.m_serverSupportedGroups = m_tlsServer.GetSupportedGroups();
+
+ if (ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(serverVersion))
+ {
+ // See RFC 8446 D.4.
+ m_recordStream.SetIgnoreChangeCipherSpec(true);
+
+ m_recordStream.SetWriteVersion(ProtocolVersion.TLSv12);
+
+ return Generate13ServerHello(clientHello, false);
+ }
+
+ m_recordStream.SetWriteVersion(serverVersion);
+
+ this.m_clientExtensions = clientHello.Extensions;
+
+ byte[] clientRenegExtData = TlsUtilities.GetExtensionData(m_clientExtensions, ExtensionType.renegotiation_info);
+
+ // NOT renegotiating
+ {
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake (both full and session-resumption)
+ */
+
+ /*
+ * 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(m_offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ {
+ securityParameters.m_secureRenegotiation = true;
+ }
+
+ /*
+ * The server MUST check if the "renegotiation_info" extension is included in the
+ * ClientHello.
+ */
+ if (clientRenegExtData != 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.
+ */
+ securityParameters.m_secureRenegotiation = true;
+
+ if (!Arrays.ConstantTimeAreEqual(clientRenegExtData,
+ CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ m_tlsServer.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
+
+ bool offeredExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
+ m_clientExtensions);
+
+ if (m_clientExtensions != null)
+ {
+ // NOTE: Validates the padding extension data, if present
+ TlsExtensionsUtilities.GetPaddingExtension(m_clientExtensions);
+
+ securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
+ m_clientExtensions);
+
+ /*
+ * 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))
+ {
+ TlsUtilities.EstablishClientSigAlgs(securityParameters, m_clientExtensions);
+ }
+
+ securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
+ m_clientExtensions);
+
+ m_tlsServer.ProcessClientExtensions(m_clientExtensions);
+ }
+
+ this.m_resumedSession = EstablishSession(m_tlsServer.GetSessionToResume(clientHello.SessionID));
+
+ if (!m_resumedSession)
+ {
+ byte[] newSessionID = m_tlsServer.GetNewSessionID();
+ if (null == newSessionID)
+ {
+ newSessionID = TlsUtilities.EmptyBytes;
+ }
+
+ this.m_tlsSession = TlsUtilities.ImportSession(newSessionID, null);
+ this.m_sessionParameters = null;
+ this.m_sessionMasterSecret = null;
+ }
+
+ securityParameters.m_sessionID = m_tlsSession.SessionID;
+
+ m_tlsServer.NotifySession(m_tlsSession);
+
+ TlsUtilities.NegotiatedVersionTlsServer(m_tlsServerContext);
+
+ {
+ bool useGmtUnixTime = m_tlsServer.ShouldUseGmtUnixTime();
+
+ securityParameters.m_serverRandom = CreateRandomBlock(useGmtUnixTime, m_tlsServerContext);
+
+ if (!serverVersion.Equals(ProtocolVersion.GetLatestTls(m_tlsServer.GetProtocolVersions())))
+ {
+ TlsUtilities.WriteDowngradeMarker(serverVersion, securityParameters.ServerRandom);
+ }
+ }
+
+ {
+ int cipherSuite = m_resumedSession
+ ? m_sessionParameters.CipherSuite
+ : m_tlsServer.GetSelectedCipherSuite();
+
+ if (!TlsUtilities.IsValidCipherSuiteSelection(m_offeredCipherSuites, cipherSuite) ||
+ !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, serverVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
+ }
+
+ m_tlsServerContext.SetRsaPreMasterSecretVersion(clientLegacyVersion);
+
+ {
+ IDictionary sessionServerExtensions = m_resumedSession
+ ? m_sessionParameters.ReadServerExtensions()
+ : m_tlsServer.GetServerExtensions();
+
+ this.m_serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(sessionServerExtensions);
+ }
+
+ m_tlsServer.GetServerExtensionsForConnection(m_serverExtensions);
+
+ // NOT renegotiating
+ {
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake (both full and session-resumption)
+ */
+ if (securityParameters.IsSecureRenegotiation)
+ {
+ byte[] serverRenegExtData = TlsUtilities.GetExtensionData(m_serverExtensions,
+ ExtensionType.renegotiation_info);
+ bool noRenegExt = (null == serverRenegExtData);
+
+ 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.m_serverExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(
+ TlsUtilities.EmptyBytes);
+ }
+ }
+ }
+
+ /*
+ * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+ * master secret [..]. (and see 5.2, 5.3)
+ */
+ if (m_resumedSession)
+ {
+ if (!m_sessionParameters.IsExtendedMasterSecret)
+ {
+ /*
+ * TODO[resumption] ProvTlsServer currently only resumes EMS sessions. Revisit this
+ * in relation to 'tlsServer.allowLegacyResumption()'.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (!offeredExtendedMasterSecret)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ securityParameters.m_extendedMasterSecret = true;
+
+ TlsExtensionsUtilities.AddExtendedMasterSecretExtension(m_serverExtensions);
+ }
+ else
+ {
+ securityParameters.m_extendedMasterSecret = offeredExtendedMasterSecret && !serverVersion.IsSsl
+ && m_tlsServer.ShouldUseExtendedMasterSecret();
+
+ if (securityParameters.IsExtendedMasterSecret)
+ {
+ TlsExtensionsUtilities.AddExtendedMasterSecretExtension(m_serverExtensions);
+ }
+ else if (m_tlsServer.RequiresExtendedMasterSecret())
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(m_serverExtensions);
+ securityParameters.m_applicationProtocolSet = true;
+
+ if (m_serverExtensions.Count > 0)
+ {
+ securityParameters.m_encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(
+ m_serverExtensions);
+
+ securityParameters.m_maxFragmentLength = ProcessMaxFragmentLengthExtension(m_clientExtensions,
+ m_serverExtensions, AlertDescription.internal_error);
+
+ securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension(
+ m_serverExtensions);
+
+ if (!m_resumedSession)
+ {
+ if (TlsUtilities.HasExpectedEmptyExtensionData(m_serverExtensions, ExtensionType.status_request_v2,
+ AlertDescription.internal_error))
+ {
+ securityParameters.m_statusRequestVersion = 2;
+ }
+ else if (TlsUtilities.HasExpectedEmptyExtensionData(m_serverExtensions, ExtensionType.status_request,
+ AlertDescription.internal_error))
+ {
+ securityParameters.m_statusRequestVersion = 1;
+ }
+
+ this.m_expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(m_serverExtensions,
+ ExtensionType.session_ticket, AlertDescription.internal_error);
+ }
+ }
+
+ ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
+
+ return new ServerHello(serverVersion, securityParameters.ServerRandom, m_tlsSession.SessionID,
+ securityParameters.CipherSuite, m_serverExtensions);
+ }
+
+ protected override TlsContext Context
+ {
+ get { return m_tlsServerContext; }
+ }
+
+ internal override AbstractTlsContext ContextAdmin
+ {
+ get { return m_tlsServerContext; }
+ }
+
+ protected override TlsPeer Peer
+ {
+ get { return m_tlsServer; }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Handle13HandshakeMessage(short type, HandshakeMessageInput buf)
+ {
+ if (!IsTlsV13ConnectionState())
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (m_resumedSession)
+ {
+ /*
+ * TODO[tls13] Abbreviated handshakes (PSK resumption)
+ *
+ * NOTE: No CertificateRequest, Certificate, CertificateVerify messages, but client
+ * might now send EndOfEarlyData after receiving server Finished message.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_FINISHED:
+ {
+ Receive13ClientCertificate(buf);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate_verify:
+ {
+ switch (m_connectionState)
+ {
+ case CS_CLIENT_CERTIFICATE:
+ {
+ Receive13ClientCertificateVerify(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.client_hello:
+ {
+ switch (m_connectionState)
+ {
+ case CS_START:
+ {
+ // NOTE: Legacy handler should be dispatching initial ClientHello.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ case CS_SERVER_HELLO_RETRY_REQUEST:
+ {
+ ClientHello clientHelloRetry = ReceiveClientHelloMessage(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_CLIENT_HELLO_RETRY;
+
+ ServerHello serverHello = Generate13ServerHello(clientHelloRetry, true);
+ SendServerHelloMessage(serverHello);
+ this.m_connectionState = CS_SERVER_HELLO;
+
+ Send13ServerHelloCoda(serverHello, true);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_FINISHED:
+ case CS_CLIENT_CERTIFICATE:
+ case CS_CLIENT_CERTIFICATE_VERIFY:
+ {
+ if (m_connectionState == CS_SERVER_FINISHED)
+ {
+ Skip13ClientCertificate();
+ }
+ if (m_connectionState != CS_CLIENT_CERTIFICATE_VERIFY)
+ {
+ Skip13ClientCertificateVerify();
+ }
+
+ Receive13ClientFinished(buf);
+ this.m_connectionState = CS_CLIENT_FINISHED;
+
+ // See RFC 8446 D.4.
+ m_recordStream.SetIgnoreChangeCipherSpec(false);
+
+ // NOTE: Completes the switch to application-data phase (server entered after CS_SERVER_FINISHED).
+ m_recordStream.EnablePendingCipherRead(false);
+
+ CompleteHandshake();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.key_update:
+ {
+ Receive13KeyUpdate(buf);
+ break;
+ }
+
+ case HandshakeType.certificate_request:
+ case HandshakeType.certificate_status:
+ case HandshakeType.certificate_url:
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.encrypted_extensions:
+ case HandshakeType.end_of_early_data:
+ case HandshakeType.hello_request:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.message_hash:
+ case HandshakeType.new_session_ticket:
+ case HandshakeType.server_hello:
+ case HandshakeType.server_hello_done:
+ case HandshakeType.server_key_exchange:
+ case HandshakeType.supplemental_data:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ protected override void HandleHandshakeMessage(short type, HandshakeMessageInput buf)
+ {
+ SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
+
+ if (m_connectionState > CS_CLIENT_HELLO
+ && TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
+ {
+ Handle13HandshakeMessage(type, buf);
+ return;
+ }
+
+ if (!IsLegacyConnectionState())
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (m_resumedSession)
+ {
+ if (type != HandshakeType.finished || m_connectionState != CS_SERVER_FINISHED)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ProcessFinishedMessage(buf);
+ this.m_connectionState = CS_CLIENT_FINISHED;
+
+ CompleteHandshake();
+ return;
+ }
+
+ switch (type)
+ {
+ case HandshakeType.client_hello:
+ {
+ if (IsApplicationDataReady)
+ {
+ RefuseRenegotiation();
+ break;
+ }
+
+ switch (m_connectionState)
+ {
+ case CS_END:
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ case CS_START:
+ {
+ ClientHello clientHello = ReceiveClientHelloMessage(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_CLIENT_HELLO;
+
+ ServerHello serverHello = GenerateServerHello(clientHello);
+ m_handshakeHash.NotifyPrfDetermined();
+
+ if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
+ {
+ if (serverHello.IsHelloRetryRequest())
+ {
+ TlsUtilities.AdjustTranscriptForRetry(m_handshakeHash);
+ SendServerHelloMessage(serverHello);
+ this.m_connectionState = CS_SERVER_HELLO_RETRY_REQUEST;
+
+ // See RFC 8446 D.4.
+ SendChangeCipherSpecMessage();
+ }
+ else
+ {
+ SendServerHelloMessage(serverHello);
+ this.m_connectionState = CS_SERVER_HELLO;
+
+ // See RFC 8446 D.4.
+ SendChangeCipherSpecMessage();
+
+ Send13ServerHelloCoda(serverHello, false);
+ }
+ break;
+ }
+
+ SendServerHelloMessage(serverHello);
+ this.m_connectionState = CS_SERVER_HELLO;
+
+ if (m_resumedSession)
+ {
+ securityParameters.m_masterSecret = m_sessionMasterSecret;
+ m_recordStream.SetPendingCipher(TlsUtilities.InitCipher(m_tlsServerContext));
+
+ SendChangeCipherSpec();
+ SendFinishedMessage();
+ this.m_connectionState = CS_SERVER_FINISHED;
+ break;
+ }
+
+ IList serverSupplementalData = m_tlsServer.GetServerSupplementalData();
+ if (serverSupplementalData != null)
+ {
+ SendSupplementalDataMessage(serverSupplementalData);
+ this.m_connectionState = CS_SERVER_SUPPLEMENTAL_DATA;
+ }
+
+ this.m_keyExchange = TlsUtilities.InitKeyExchangeServer(m_tlsServerContext, m_tlsServer);
+
+ TlsCredentials serverCredentials = TlsUtilities.EstablishServerCredentials(m_tlsServer);
+
+ // Server certificate
+ {
+ Certificate serverCertificate = null;
+
+ MemoryStream endPointHash = new MemoryStream();
+ if (null == serverCredentials)
+ {
+ m_keyExchange.SkipServerCredentials();
+ }
+ else
+ {
+ m_keyExchange.ProcessServerCredentials(serverCredentials);
+
+ serverCertificate = serverCredentials.Certificate;
+ SendCertificateMessage(serverCertificate, endPointHash);
+ this.m_connectionState = CS_SERVER_CERTIFICATE;
+ }
+
+ securityParameters.m_tlsServerEndPoint = endPointHash.ToArray();
+
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes
+ // CertificateStatus
+ if (null == serverCertificate || serverCertificate.IsEmpty)
+ {
+ securityParameters.m_statusRequestVersion = 0;
+ }
+ }
+
+ if (securityParameters.StatusRequestVersion > 0)
+ {
+ CertificateStatus certificateStatus = m_tlsServer.GetCertificateStatus();
+ if (certificateStatus != null)
+ {
+ SendCertificateStatusMessage(certificateStatus);
+ this.m_connectionState = CS_SERVER_CERTIFICATE_STATUS;
+ }
+ }
+
+ byte[] serverKeyExchange = m_keyExchange.GenerateServerKeyExchange();
+ if (serverKeyExchange != null)
+ {
+ SendServerKeyExchangeMessage(serverKeyExchange);
+ this.m_connectionState = CS_SERVER_KEY_EXCHANGE;
+ }
+
+ if (null != serverCredentials)
+ {
+ this.m_certificateRequest = m_tlsServer.GetCertificateRequest();
+
+ if (null == m_certificateRequest)
+ {
+ /*
+ * For static agreement key exchanges, CertificateRequest is required since
+ * the client Certificate message is mandatory but can only be sent if the
+ * server requests it.
+ */
+ if (!m_keyExchange.RequiresCertificateVerify)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ else
+ {
+ if (TlsUtilities.IsTlsV12(m_tlsServerContext)
+ != (m_certificateRequest.SupportedSignatureAlgorithms != null))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.m_certificateRequest = TlsUtilities.ValidateCertificateRequest(m_certificateRequest,
+ m_keyExchange);
+
+ TlsUtilities.EstablishServerSigAlgs(securityParameters, m_certificateRequest);
+
+ TlsUtilities.TrackHashAlgorithms(m_handshakeHash, securityParameters.ServerSigAlgs);
+
+ SendCertificateRequestMessage(m_certificateRequest);
+ this.m_connectionState = CS_SERVER_CERTIFICATE_REQUEST;
+ }
+ }
+
+ SendServerHelloDoneMessage();
+ this.m_connectionState = CS_SERVER_HELLO_DONE;
+
+ bool forceBuffering = false;
+ TlsUtilities.SealHandshakeHash(m_tlsServerContext, m_handshakeHash, forceBuffering);
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.supplemental_data:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ {
+ m_tlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf));
+ this.m_connectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ {
+ if (m_connectionState != CS_CLIENT_SUPPLEMENTAL_DATA)
+ {
+ m_tlsServer.ProcessClientSupplementalData(null);
+ }
+
+ if (m_certificateRequest == null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ReceiveCertificateMessage(buf);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.client_key_exchange:
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ case CS_CLIENT_CERTIFICATE:
+ {
+ if (m_connectionState == CS_SERVER_HELLO_DONE)
+ {
+ m_tlsServer.ProcessClientSupplementalData(null);
+ }
+ if (m_connectionState != CS_CLIENT_CERTIFICATE)
+ {
+ if (null == m_certificateRequest)
+ {
+ m_keyExchange.SkipClientCredentials();
+ }
+ else if (TlsUtilities.IsTlsV12(m_tlsServerContext))
+ {
+ /*
+ * 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(m_tlsServerContext))
+ {
+ /*
+ * 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.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ else
+ {
+ NotifyClientCertificate(Certificate.EmptyChain);
+ }
+ }
+
+ ReceiveClientKeyExchangeMessage(buf);
+ this.m_connectionState = CS_CLIENT_KEY_EXCHANGE;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate_verify:
+ {
+ switch (m_connectionState)
+ {
+ 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);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (m_connectionState)
+ {
+ case CS_CLIENT_KEY_EXCHANGE:
+ case CS_CLIENT_CERTIFICATE_VERIFY:
+ {
+ if (m_connectionState != CS_CLIENT_CERTIFICATE_VERIFY)
+ {
+ if (ExpectCertificateVerifyMessage())
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ ProcessFinishedMessage(buf);
+ buf.UpdateHash(m_handshakeHash);
+ this.m_connectionState = CS_CLIENT_FINISHED;
+
+ if (m_expectSessionTicket)
+ {
+ SendNewSessionTicketMessage(m_tlsServer.GetNewSessionTicket());
+ this.m_connectionState = CS_SERVER_SESSION_TICKET;
+ }
+
+ SendChangeCipherSpec();
+ SendFinishedMessage();
+ this.m_connectionState = CS_SERVER_FINISHED;
+
+ CompleteHandshake();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+
+ case HandshakeType.certificate_request:
+ case HandshakeType.certificate_status:
+ case HandshakeType.certificate_url:
+ case HandshakeType.encrypted_extensions:
+ case HandshakeType.end_of_early_data:
+ case HandshakeType.hello_request:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.key_update:
+ case HandshakeType.message_hash:
+ case HandshakeType.new_session_ticket:
+ case HandshakeType.server_hello:
+ case HandshakeType.server_hello_done:
+ case HandshakeType.server_key_exchange:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ protected override void HandleAlertWarningMessage(short alertDescription)
+ {
+ /*
+ * 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 (AlertDescription.no_certificate == alertDescription && null != m_certificateRequest
+ && TlsUtilities.IsSsl(m_tlsServerContext))
+ {
+ switch (m_connectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ {
+ if (m_connectionState != CS_CLIENT_SUPPLEMENTAL_DATA)
+ {
+ m_tlsServer.ProcessClientSupplementalData(null);
+ }
+
+ NotifyClientCertificate(Certificate.EmptyChain);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE;
+ return;
+ }
+ }
+ }
+
+ base.HandleAlertWarningMessage(alertDescription);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ if (null == m_certificateRequest)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsUtilities.ProcessClientCertificate(m_tlsServerContext, clientCertificate, m_keyExchange, m_tlsServer);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13ClientCertificate(MemoryStream buf)
+ {
+ // TODO[tls13] This currently just duplicates 'receiveCertificateMessage'
+
+ Certificate.ParseOptions options = new Certificate.ParseOptions()
+ .SetMaxChainLength(m_tlsServer.GetMaxCertificateChainLength());
+
+ Certificate clientCertificate = Certificate.Parse(options, m_tlsServerContext, buf, null);
+
+ AssertEmpty(buf);
+
+ NotifyClientCertificate(clientCertificate);
+ }
+
+ /// <exception cref="IOException"/>
+ protected void Receive13ClientCertificateVerify(MemoryStream buf)
+ {
+ Certificate clientCertificate = m_tlsServerContext.SecurityParameters.PeerCertificate;
+ if (null == clientCertificate || clientCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ // TODO[tls13] Actual structure is 'CertificateVerify' in RFC 8446, consider adding for clarity
+ DigitallySigned certificateVerify = DigitallySigned.Parse(m_tlsServerContext, buf);
+
+ AssertEmpty(buf);
+
+ TlsUtilities.Verify13CertificateVerifyClient(m_tlsServerContext, m_certificateRequest, certificateVerify,
+ m_handshakeHash);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Receive13ClientFinished(MemoryStream buf)
+ {
+ Process13FinishedMessage(buf);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ReceiveCertificateMessage(MemoryStream buf)
+ {
+ Certificate.ParseOptions options = new Certificate.ParseOptions()
+ .SetMaxChainLength(m_tlsServer.GetMaxCertificateChainLength());
+
+ Certificate clientCertificate = Certificate.Parse(options, m_tlsServerContext, buf, null);
+
+ AssertEmpty(buf);
+
+ NotifyClientCertificate(clientCertificate);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf)
+ {
+ DigitallySigned certificateVerify = DigitallySigned.Parse(m_tlsServerContext, buf);
+
+ AssertEmpty(buf);
+
+ TlsUtilities.VerifyCertificateVerifyClient(m_tlsServerContext, m_certificateRequest, certificateVerify,
+ m_handshakeHash);
+
+ this.m_handshakeHash = m_handshakeHash.StopTracking();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual ClientHello ReceiveClientHelloMessage(MemoryStream buf)
+ {
+ return ClientHello.Parse(buf, null);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ReceiveClientKeyExchangeMessage(MemoryStream buf)
+ {
+ m_keyExchange.ProcessClientKeyExchange(buf);
+
+ AssertEmpty(buf);
+
+ bool isSsl = TlsUtilities.IsSsl(m_tlsServerContext);
+ if (isSsl)
+ {
+ // NOTE: For SSLv3 (only), master_secret needed to calculate session hash
+ EstablishMasterSecret(m_tlsServerContext, m_keyExchange);
+ }
+
+ m_tlsServerContext.SecurityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
+
+ if (!isSsl)
+ {
+ // NOTE: For (D)TLS, session hash potentially needed for extended_master_secret
+ EstablishMasterSecret(m_tlsServerContext, m_keyExchange);
+ }
+
+ m_recordStream.SetPendingCipher(TlsUtilities.InitCipher(m_tlsServerContext));
+
+ if (!ExpectCertificateVerifyMessage())
+ {
+ this.m_handshakeHash = m_handshakeHash.StopTracking();
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13EncryptedExtensionsMessage(IDictionary serverExtensions)
+ {
+ // TODO[tls13] Avoid extra copy; use placeholder to write opaque-16 data directly to message buffer
+
+ byte[] extBytes = WriteExtensionsData(serverExtensions);
+
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.encrypted_extensions);
+ TlsUtilities.WriteOpaque16(extBytes, message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Send13ServerHelloCoda(ServerHello serverHello, bool afterHelloRetryRequest)
+ {
+ SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
+
+ byte[] serverHelloTranscriptHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
+
+ TlsUtilities.Establish13PhaseHandshake(m_tlsServerContext, serverHelloTranscriptHash, m_recordStream);
+
+ m_recordStream.EnablePendingCipherWrite();
+ m_recordStream.EnablePendingCipherRead(true);
+
+ Send13EncryptedExtensionsMessage(m_serverExtensions);
+ this.m_connectionState = CS_SERVER_ENCRYPTED_EXTENSIONS;
+
+ // CertificateRequest
+ {
+ this.m_certificateRequest = m_tlsServer.GetCertificateRequest();
+ if (null != m_certificateRequest)
+ {
+ if (!m_certificateRequest.HasCertificateRequestContext(TlsUtilities.EmptyBytes))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsUtilities.EstablishServerSigAlgs(securityParameters, m_certificateRequest);
+
+ SendCertificateRequestMessage(m_certificateRequest);
+ this.m_connectionState = CS_SERVER_CERTIFICATE_REQUEST;
+ }
+ }
+
+ /*
+ * TODO[tls13] For PSK-only key exchange, there's no Certificate message.
+ */
+
+ TlsCredentialedSigner serverCredentials = TlsUtilities.Establish13ServerCredentials(m_tlsServer);
+ if (null == serverCredentials)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ // Certificate
+ {
+ /*
+ * TODO[tls13] Note that we are expecting the TlsServer implementation to take care of
+ * e.g. adding optional "status_request" extension to each CertificateEntry.
+ */
+ /*
+ * No CertificateStatus message is sent; TLS 1.3 uses per-CertificateEntry
+ * "status_request" extension instead.
+ */
+
+ Certificate serverCertificate = serverCredentials.Certificate;
+ Send13CertificateMessage(serverCertificate);
+ securityParameters.m_tlsServerEndPoint = null;
+ this.m_connectionState = CS_SERVER_CERTIFICATE;
+ }
+
+ // CertificateVerify
+ {
+ DigitallySigned certificateVerify = TlsUtilities.Generate13CertificateVerify(m_tlsServerContext,
+ serverCredentials, m_handshakeHash);
+ Send13CertificateVerifyMessage(certificateVerify);
+ this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
+ }
+
+ // Finished
+ {
+ Send13FinishedMessage();
+ this.m_connectionState = CS_SERVER_FINISHED;
+ }
+
+ byte[] serverFinishedTranscriptHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
+
+ TlsUtilities.Establish13PhaseApplication(m_tlsServerContext, serverFinishedTranscriptHash, m_recordStream);
+
+ m_recordStream.EnablePendingCipherWrite();
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_request);
+ certificateRequest.Encode(m_tlsServerContext, message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendCertificateStatusMessage(CertificateStatus certificateStatus)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_status);
+ // TODO[tls13] Ensure this cannot happen for (D)TLS1.3+
+ certificateStatus.Encode(message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendHelloRequestMessage()
+ {
+ HandshakeMessageOutput.Send(this, HandshakeType.hello_request, TlsUtilities.EmptyBytes);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendNewSessionTicketMessage(NewSessionTicket newSessionTicket)
+ {
+ if (newSessionTicket == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.new_session_ticket);
+ newSessionTicket.Encode(message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendServerHelloDoneMessage()
+ {
+ HandshakeMessageOutput.Send(this, HandshakeType.server_hello_done, TlsUtilities.EmptyBytes);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendServerHelloMessage(ServerHello serverHello)
+ {
+ HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.server_hello);
+ serverHello.Encode(m_tlsServerContext, message);
+ message.Send(this);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void SendServerKeyExchangeMessage(byte[] serverKeyExchange)
+ {
+ HandshakeMessageOutput.Send(this, HandshakeType.server_key_exchange, serverKeyExchange);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Skip13ClientCertificate()
+ {
+ if (null != m_certificateRequest)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void Skip13ClientCertificateVerify()
+ {
+ if (ExpectCertificateVerifyMessage())
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsSession.cs b/crypto/src/tls/TlsSession.cs
new file mode 100644
index 000000000..bc68757c2
--- /dev/null
+++ b/crypto/src/tls/TlsSession.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for a carrier object for a TLS session.</summary>
+ public interface TlsSession
+ {
+ SessionParameters ExportSessionParameters();
+
+ byte[] SessionID { get; }
+
+ void Invalidate();
+
+ bool IsResumable { get; }
+ }
+}
diff --git a/crypto/src/tls/TlsSessionImpl.cs b/crypto/src/tls/TlsSessionImpl.cs
new file mode 100644
index 000000000..a4eb6b94a
--- /dev/null
+++ b/crypto/src/tls/TlsSessionImpl.cs
@@ -0,0 +1,52 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class TlsSessionImpl
+ : TlsSession
+ {
+ private readonly byte[] m_sessionID;
+ private readonly SessionParameters m_sessionParameters;
+ private bool m_resumable;
+
+ internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ if (sessionID == null)
+ throw new ArgumentNullException("sessionID");
+ if (sessionID.Length > 32)
+ throw new ArgumentException("cannot be longer than 32 bytes", "sessionID");
+
+ this.m_sessionID = Arrays.Clone(sessionID);
+ this.m_sessionParameters = sessionParameters;
+ this.m_resumable = sessionID.Length > 0 && null != sessionParameters;
+ }
+
+ public SessionParameters ExportSessionParameters()
+ {
+ lock (this)
+ {
+ return m_sessionParameters == null ? null : m_sessionParameters.Copy();
+ }
+ }
+
+ public byte[] SessionID
+ {
+ get { lock (this) return m_sessionID; }
+ }
+
+ public void Invalidate()
+ {
+ lock (this)
+ {
+ this.m_resumable = false;
+ }
+ }
+
+ public bool IsResumable
+ {
+ get { lock (this) return m_resumable; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsSrpConfigVerifier.cs b/crypto/src/tls/TlsSrpConfigVerifier.cs
new file mode 100644
index 000000000..e4d83ab24
--- /dev/null
+++ b/crypto/src/tls/TlsSrpConfigVerifier.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Tls.Crypto;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Interface for verifying SRP config needs to conform to.</summary>
+ public interface TlsSrpConfigVerifier
+ {
+ /// <summary>Check whether the given SRP configuration is acceptable for use.</summary>
+ /// <param name="srpConfig">the <see cref="TlsSrpConfig"/> to check.</param>
+ /// <returns>true if (and only if) the specified configuration is acceptable.</returns>
+ bool Accept(TlsSrpConfig srpConfig);
+ }
+}
diff --git a/crypto/src/tls/TlsSrpIdentity.cs b/crypto/src/tls/TlsSrpIdentity.cs
new file mode 100644
index 000000000..c0492e4a2
--- /dev/null
+++ b/crypto/src/tls/TlsSrpIdentity.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Processor interface for an SRP identity.</summary>
+ public interface TlsSrpIdentity
+ {
+ byte[] GetSrpIdentity();
+
+ byte[] GetSrpPassword();
+ }
+}
diff --git a/crypto/src/tls/TlsSrpIdentityManager.cs b/crypto/src/tls/TlsSrpIdentityManager.cs
new file mode 100644
index 000000000..1cc2840be
--- /dev/null
+++ b/crypto/src/tls/TlsSrpIdentityManager.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>Base interface for an object that can return login parameters from an SRP identity.</summary>
+ public interface TlsSrpIdentityManager
+ {
+ /// <summary>Lookup the <see cref="TlsSrpLoginParameters"/> corresponding to the specified identity.</summary>
+ /// <remarks>
+ /// NOTE: To avoid "identity probing", unknown identities SHOULD be handled as recommended in RFC 5054 2.5.1.3.
+ /// <see cref="SimulatedTlsSrpIdentityManager"/> is provided for this purpose.
+ /// </remarks>
+ /// <param name="identity">the SRP identity sent by the connecting client.</param>
+ /// <returns>the <see cref="TlsSrpLoginParameters"/> for the specified identity, or else 'simulated' parameters
+ /// if the identity is not recognized. A null value is also allowed, but not recommended.</returns>
+ TlsSrpLoginParameters GetLoginParameters(byte[] identity);
+ }
+}
diff --git a/crypto/src/tls/TlsSrpKeyExchange.cs b/crypto/src/tls/TlsSrpKeyExchange.cs
new file mode 100644
index 000000000..835523e36
--- /dev/null
+++ b/crypto/src/tls/TlsSrpKeyExchange.cs
@@ -0,0 +1,186 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>(D)TLS SRP key exchange (RFC 5054).</summary>
+ public class TlsSrpKeyExchange
+ : AbstractTlsKeyExchange
+ {
+ private static int CheckKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.SRP:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return keyExchange;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+ }
+
+ protected TlsSrpIdentity m_srpIdentity;
+ protected TlsSrpConfigVerifier m_srpConfigVerifier;
+ protected TlsCertificate m_serverCertificate = null;
+ protected byte[] m_srpSalt = null;
+ protected TlsSrp6Client m_srpClient = null;
+
+ protected TlsSrpLoginParameters m_srpLoginParameters;
+ protected TlsCredentialedSigner m_serverCredentials = null;
+ protected TlsSrp6Server m_srpServer = null;
+
+ protected BigInteger m_srpPeerCredentials = null;
+
+ public TlsSrpKeyExchange(int keyExchange, TlsSrpIdentity srpIdentity, TlsSrpConfigVerifier srpConfigVerifier)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_srpIdentity = srpIdentity;
+ this.m_srpConfigVerifier = srpConfigVerifier;
+ }
+
+ public TlsSrpKeyExchange(int keyExchange, TlsSrpLoginParameters srpLoginParameters)
+ : base(CheckKeyExchange(keyExchange))
+ {
+ this.m_srpLoginParameters = srpLoginParameters;
+ }
+
+ public override void SkipServerCredentials()
+ {
+ if (m_keyExchange != KeyExchangeAlgorithm.SRP)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ if (m_keyExchange == KeyExchangeAlgorithm.SRP)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_serverCredentials = TlsUtilities.RequireSignerCredentials(serverCredentials);
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ if (m_keyExchange == KeyExchangeAlgorithm.SRP)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_serverCertificate = serverCertificate.GetCertificateAt(0);
+ }
+
+ public override bool RequiresServerKeyExchange
+ {
+ get { return true; }
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ TlsSrpConfig config = m_srpLoginParameters.Config;
+
+ this.m_srpServer = m_context.Crypto.CreateSrp6Server(config, m_srpLoginParameters.Verifier);
+
+ BigInteger B = m_srpServer.GenerateServerCredentials();
+
+ BigInteger[] ng = config.GetExplicitNG();
+ ServerSrpParams srpParams = new ServerSrpParams(ng[0], ng[1], m_srpLoginParameters.Salt, B);
+
+ DigestInputBuffer digestBuffer = new DigestInputBuffer();
+
+ srpParams.Encode(digestBuffer);
+
+ if (m_serverCredentials != null)
+ {
+ TlsUtilities.GenerateServerKeyExchangeSignature(m_context, m_serverCredentials, digestBuffer);
+ }
+
+ return digestBuffer.ToArray();
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ DigestInputBuffer digestBuffer = null;
+ Stream teeIn = input;
+
+ if (m_keyExchange != KeyExchangeAlgorithm.SRP)
+ {
+ digestBuffer = new DigestInputBuffer();
+ teeIn = new TeeInputStream(input, digestBuffer);
+ }
+
+ ServerSrpParams srpParams = ServerSrpParams.Parse(teeIn);
+
+ if (digestBuffer != null)
+ {
+ TlsUtilities.VerifyServerKeyExchangeSignature(m_context, input, m_serverCertificate, digestBuffer);
+ }
+
+ TlsSrpConfig config = new TlsSrpConfig();
+ config.SetExplicitNG(new BigInteger[]{ srpParams.N, srpParams.G });
+
+ if (!m_srpConfigVerifier.Accept(config))
+ throw new TlsFatalAlert(AlertDescription.insufficient_security);
+
+ this.m_srpSalt = srpParams.S;
+
+ /*
+ * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
+ * B % N = 0.
+ */
+ this.m_srpPeerCredentials = ValidatePublicValue(srpParams.N, srpParams.B);
+ this.m_srpClient = m_context.Crypto.CreateSrp6Client(config);
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override void GenerateClientKeyExchange(Stream output)
+ {
+ byte[] identity = m_srpIdentity.GetSrpIdentity();
+ byte[] password = m_srpIdentity.GetSrpPassword();
+
+ BigInteger A = m_srpClient.GenerateClientCredentials(m_srpSalt, identity, password);
+ TlsSrpUtilities.WriteSrpParameter(A, output);
+
+ m_context.SecurityParameters.m_srpIdentity = Arrays.Clone(identity);
+ }
+
+ public override void ProcessClientKeyExchange(Stream input)
+ {
+ /*
+ * RFC 5054 2.5.4: The server MUST abort the handshake with an "illegal_parameter" alert if
+ * A % N = 0.
+ */
+ this.m_srpPeerCredentials = ValidatePublicValue(m_srpLoginParameters.Config.GetExplicitNG()[0],
+ TlsSrpUtilities.ReadSrpParameter(input));
+
+ m_context.SecurityParameters.m_srpIdentity = Arrays.Clone(m_srpLoginParameters.Identity);
+ }
+
+ public override TlsSecret GeneratePreMasterSecret()
+ {
+ BigInteger S = m_srpServer != null
+ ? m_srpServer.CalculateSecret(m_srpPeerCredentials)
+ : m_srpClient.CalculateSecret(m_srpPeerCredentials);
+
+ // TODO Check if this needs to be a fixed size
+ return m_context.Crypto.CreateSecret(BigIntegers.AsUnsignedByteArray(S));
+ }
+
+ protected static BigInteger ValidatePublicValue(BigInteger N, BigInteger val)
+ {
+ val = val.Mod(N);
+
+ // Check that val % N != 0
+ if (val.Equals(BigInteger.Zero))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return val;
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsSrpLoginParameters.cs b/crypto/src/tls/TlsSrpLoginParameters.cs
new file mode 100644
index 000000000..2dda942a3
--- /dev/null
+++ b/crypto/src/tls/TlsSrpLoginParameters.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsSrpLoginParameters
+ {
+ protected byte[] m_identity;
+ protected TlsSrpConfig m_srpConfig;
+ protected BigInteger m_verifier;
+ protected byte[] m_salt;
+
+ public TlsSrpLoginParameters(byte[] identity, TlsSrpConfig srpConfig, BigInteger verifier, byte[] salt)
+ {
+ this.m_identity = Arrays.Clone(identity);
+ this.m_srpConfig = srpConfig;
+ this.m_verifier = verifier;
+ this.m_salt = Arrays.Clone(salt);
+ }
+
+ public virtual TlsSrpConfig Config
+ {
+ get { return m_srpConfig; }
+ }
+
+ public virtual byte[] Identity
+ {
+ get { return m_identity; }
+ }
+
+ public virtual byte[] Salt
+ {
+ get { return m_salt; }
+ }
+
+ public virtual BigInteger Verifier
+ {
+ get { return m_verifier; }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsSrpUtilities.cs b/crypto/src/tls/TlsSrpUtilities.cs
new file mode 100644
index 000000000..c36a667ac
--- /dev/null
+++ b/crypto/src/tls/TlsSrpUtilities.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class TlsSrpUtilities
+ {
+ /// <exception cref="IOException"/>
+ public static void AddSrpExtension(IDictionary extensions, byte[] identity)
+ {
+ extensions[ExtensionType.srp] = CreateSrpExtension(identity);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] GetSrpExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.srp);
+ return extensionData == null ? null : ReadSrpExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] CreateSrpExtension(byte[] identity)
+ {
+ if (identity == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeOpaque8(identity);
+ }
+
+ /// <exception cref="IOException"/>
+ public static byte[] ReadSrpExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ return TlsUtilities.DecodeOpaque8(extensionData, 1);
+ }
+
+ /// <exception cref="IOException"/>
+ public static BigInteger ReadSrpParameter(Stream input)
+ {
+ return new BigInteger(1, TlsUtilities.ReadOpaque16(input, 1));
+ }
+
+ /// <exception cref="IOException"/>
+ public static void WriteSrpParameter(BigInteger x, Stream output)
+ {
+ TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output);
+ }
+
+ public static bool IsSrpCipherSuite(int cipherSuite)
+ {
+ switch (TlsUtilities.GetKeyExchangeAlgorithm(cipherSuite))
+ {
+ case KeyExchangeAlgorithm.SRP:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsSrtpUtilities.cs b/crypto/src/tls/TlsSrtpUtilities.cs
new file mode 100644
index 000000000..72a9e774b
--- /dev/null
+++ b/crypto/src/tls/TlsSrtpUtilities.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5764 DTLS Extension to Establish Keys for SRTP.</summary>
+ public abstract class TlsSrtpUtilities
+{
+ /// <exception cref="IOException"/>
+ public static void AddUseSrtpExtension(IDictionary extensions, UseSrtpData useSrtpData)
+ {
+ extensions[ExtensionType.use_srtp] = CreateUseSrtpExtension(useSrtpData);
+ }
+
+ /// <exception cref="IOException"/>
+ public static UseSrtpData GetUseSrtpExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.use_srtp);
+ return extensionData == null ? null : ReadUseSrtpExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
+ 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();
+ }
+
+ /// <exception cref="IOException"/>
+ public static UseSrtpData ReadUseSrtpExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ // 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/tls/TlsStream.cs b/crypto/src/tls/TlsStream.cs
new file mode 100644
index 000000000..9ca88a2a7
--- /dev/null
+++ b/crypto/src/tls/TlsStream.cs
@@ -0,0 +1,96 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ internal class TlsStream
+ : Stream
+ {
+ private readonly TlsProtocol m_handler;
+
+ internal TlsStream(TlsProtocol handler)
+ {
+ this.m_handler = handler;
+ }
+
+ public override bool CanRead
+ {
+ get { return !m_handler.IsClosed; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return !m_handler.IsClosed; }
+ }
+
+#if PORTABLE
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ m_handler.Close();
+ }
+ base.Dispose(disposing);
+ }
+#else
+ public override void Close()
+ {
+ m_handler.Close();
+ base.Close();
+ }
+#endif
+
+ public override void Flush()
+ {
+ m_handler.Flush();
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return m_handler.ReadApplicationData(buf, off, len);
+ }
+
+ public override int ReadByte()
+ {
+ byte[] buf = new byte[1];
+ int ret = Read(buf, 0, 1);
+ return ret <= 0 ? -1 : buf[0];
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ m_handler.WriteApplicationData(buf, off, len);
+ }
+
+ public override void WriteByte(byte b)
+ {
+ Write(new byte[]{ b }, 0, 1);
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsTimeoutException.cs b/crypto/src/tls/TlsTimeoutException.cs
new file mode 100644
index 000000000..ce2e1ac63
--- /dev/null
+++ b/crypto/src/tls/TlsTimeoutException.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public class TlsTimeoutException
+ : IOException
+ {
+ public TlsTimeoutException()
+ : base()
+ {
+ }
+
+ public TlsTimeoutException(string message)
+ : base(message)
+ {
+ }
+
+ public TlsTimeoutException(string message, Exception cause)
+ : base(message, cause)
+ {
+ }
+ }
+}
diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs
new file mode 100644
index 000000000..e7555ba9f
--- /dev/null
+++ b/crypto/src/tls/TlsUtilities.cs
@@ -0,0 +1,5305 @@
+using System;
+using System.Collections;
+using System.IO;
+#if !PORTABLE || DOTNET
+using System.Net.Sockets;
+#endif
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Bsi;
+using Org.BouncyCastle.Asn1.Eac;
+using Org.BouncyCastle.Asn1.EdEC;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Rosstandart;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls
+{
+ public abstract class TlsUtilities
+ {
+ private static readonly byte[] DowngradeTlsV11 = Hex.DecodeStrict("444F574E47524400");
+ private static readonly byte[] DowngradeTlsV12 = Hex.DecodeStrict("444F574E47524401");
+
+ private static readonly IDictionary CertSigAlgOids = CreateCertSigAlgOids();
+ private static readonly IList DefaultSupportedSigAlgs = CreateDefaultSupportedSigAlgs();
+
+ private static void AddCertSigAlgOid(IDictionary d, DerObjectIdentifier oid,
+ SignatureAndHashAlgorithm sigAndHash)
+ {
+ d[oid.Id] = sigAndHash;
+ }
+
+ private static void AddCertSigAlgOid(IDictionary d, DerObjectIdentifier oid, short hashAlgorithm,
+ short signatureAlgorithm)
+ {
+ AddCertSigAlgOid(d, oid, SignatureAndHashAlgorithm.GetInstance(hashAlgorithm, signatureAlgorithm));
+ }
+
+ private static IDictionary CreateCertSigAlgOids()
+ {
+ IDictionary d = Platform.CreateHashtable();
+
+ AddCertSigAlgOid(d, NistObjectIdentifiers.DsaWithSha224, HashAlgorithm.sha224, SignatureAlgorithm.dsa);
+ AddCertSigAlgOid(d, NistObjectIdentifiers.DsaWithSha256, HashAlgorithm.sha256, SignatureAlgorithm.dsa);
+ AddCertSigAlgOid(d, NistObjectIdentifiers.DsaWithSha384, HashAlgorithm.sha384, SignatureAlgorithm.dsa);
+ AddCertSigAlgOid(d, NistObjectIdentifiers.DsaWithSha512, HashAlgorithm.sha512, SignatureAlgorithm.dsa);
+
+ AddCertSigAlgOid(d, OiwObjectIdentifiers.DsaWithSha1, HashAlgorithm.sha1, SignatureAlgorithm.dsa);
+ AddCertSigAlgOid(d, OiwObjectIdentifiers.Sha1WithRsa, HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+
+ AddCertSigAlgOid(d, PkcsObjectIdentifiers.Sha1WithRsaEncryption, HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ AddCertSigAlgOid(d, PkcsObjectIdentifiers.Sha224WithRsaEncryption, HashAlgorithm.sha224, SignatureAlgorithm.rsa);
+ AddCertSigAlgOid(d, PkcsObjectIdentifiers.Sha256WithRsaEncryption, HashAlgorithm.sha256, SignatureAlgorithm.rsa);
+ AddCertSigAlgOid(d, PkcsObjectIdentifiers.Sha384WithRsaEncryption, HashAlgorithm.sha384, SignatureAlgorithm.rsa);
+ AddCertSigAlgOid(d, PkcsObjectIdentifiers.Sha512WithRsaEncryption, HashAlgorithm.sha512, SignatureAlgorithm.rsa);
+
+ AddCertSigAlgOid(d, X9ObjectIdentifiers.ECDsaWithSha1, HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, X9ObjectIdentifiers.ECDsaWithSha224, HashAlgorithm.sha224, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, X9ObjectIdentifiers.ECDsaWithSha256, HashAlgorithm.sha256, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, X9ObjectIdentifiers.ECDsaWithSha384, HashAlgorithm.sha384, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, X9ObjectIdentifiers.ECDsaWithSha512, HashAlgorithm.sha512, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, X9ObjectIdentifiers.IdDsaWithSha1, HashAlgorithm.sha1, SignatureAlgorithm.dsa);
+
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_ECDSA_SHA_1, HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_ECDSA_SHA_224, HashAlgorithm.sha224, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_ECDSA_SHA_256, HashAlgorithm.sha256, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_ECDSA_SHA_384, HashAlgorithm.sha384, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_ECDSA_SHA_512, HashAlgorithm.sha512, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ AddCertSigAlgOid(d, EacObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, HashAlgorithm.sha256, SignatureAlgorithm.rsa);
+
+ AddCertSigAlgOid(d, BsiObjectIdentifiers.ecdsa_plain_SHA1, HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, BsiObjectIdentifiers.ecdsa_plain_SHA224, HashAlgorithm.sha224, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, BsiObjectIdentifiers.ecdsa_plain_SHA256, HashAlgorithm.sha256, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, BsiObjectIdentifiers.ecdsa_plain_SHA384, HashAlgorithm.sha384, SignatureAlgorithm.ecdsa);
+ AddCertSigAlgOid(d, BsiObjectIdentifiers.ecdsa_plain_SHA512, HashAlgorithm.sha512, SignatureAlgorithm.ecdsa);
+
+ AddCertSigAlgOid(d, EdECObjectIdentifiers.id_Ed25519, SignatureAndHashAlgorithm.ed25519);
+ AddCertSigAlgOid(d, EdECObjectIdentifiers.id_Ed448, SignatureAndHashAlgorithm.ed448);
+
+ AddCertSigAlgOid(d, RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256,
+ SignatureAndHashAlgorithm.gostr34102012_256);
+ AddCertSigAlgOid(d, RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512,
+ SignatureAndHashAlgorithm.gostr34102012_512);
+
+ // TODO[RFC 8998]
+ //AddCertSigAlgOid(d, GMObjectIdentifiers.sm2sign_with_sm3, HashAlgorithm.sm3, SignatureAlgorithm.sm2);
+
+ return d;
+ }
+
+ private static IList CreateDefaultSupportedSigAlgs()
+ {
+ IList result = Platform.CreateArrayList();
+ result.Add(SignatureAndHashAlgorithm.ed25519);
+ result.Add(SignatureAndHashAlgorithm.ed448);
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha384, SignatureAlgorithm.ecdsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha512, SignatureAlgorithm.ecdsa));
+ result.Add(SignatureAndHashAlgorithm.rsa_pss_rsae_sha256);
+ result.Add(SignatureAndHashAlgorithm.rsa_pss_rsae_sha384);
+ result.Add(SignatureAndHashAlgorithm.rsa_pss_rsae_sha512);
+ result.Add(SignatureAndHashAlgorithm.rsa_pss_pss_sha256);
+ result.Add(SignatureAndHashAlgorithm.rsa_pss_pss_sha384);
+ result.Add(SignatureAndHashAlgorithm.rsa_pss_pss_sha512);
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha256, SignatureAlgorithm.rsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha384, SignatureAlgorithm.rsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha512, SignatureAlgorithm.rsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha256, SignatureAlgorithm.dsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha384, SignatureAlgorithm.dsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha512, SignatureAlgorithm.dsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha224, SignatureAlgorithm.ecdsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha224, SignatureAlgorithm.rsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha224, SignatureAlgorithm.dsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, SignatureAlgorithm.rsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, SignatureAlgorithm.dsa));
+ return result;
+ }
+
+ 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 readonly string[] EmptyStrings = new string[0];
+
+ internal static short MinimumHashStrict = HashAlgorithm.sha1;
+ internal static short MinimumHashPreferred = HashAlgorithm.sha256;
+
+ public static void CheckUint8(short i)
+ {
+ if (!IsValidUint8(i))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public static void CheckUint8(int i)
+ {
+ if (!IsValidUint8(i))
+ 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(short i)
+ {
+ return (i & 0xFF) == i;
+ }
+
+ 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;
+ }
+
+ 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 IsTlsV10(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv10.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV10(TlsContext context)
+ {
+ return IsTlsV10(context.ServerVersion);
+ }
+
+ public static bool IsTlsV11(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV11(TlsContext context)
+ {
+ return IsTlsV11(context.ServerVersion);
+ }
+
+ public static bool IsTlsV12(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV12(TlsContext context)
+ {
+ return IsTlsV12(context.ServerVersion);
+ }
+
+ public static bool IsTlsV13(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV13(TlsContext context)
+ {
+ return IsTlsV13(context.ServerVersion);
+ }
+
+ public static void WriteUint8(short i, Stream output)
+ {
+ output.WriteByte((byte)i);
+ }
+
+ public static void WriteUint8(int i, Stream output)
+ {
+ output.WriteByte((byte)i);
+ }
+
+ public static void WriteUint8(short i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)i;
+ }
+
+ public static void WriteUint8(int i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)i;
+ }
+
+ public static void WriteUint16(int i, Stream output)
+ {
+ output.WriteByte((byte)(i >> 8));
+ output.WriteByte((byte)i);
+ }
+
+ public static void WriteUint16(int i, byte[] buf, int offset)
+ {
+ buf[offset ] = (byte)(i >> 8);
+ buf[offset + 1] = (byte)i;
+ }
+
+ public static void WriteUint24(int i, Stream output)
+ {
+ output.WriteByte((byte)(i >> 16));
+ output.WriteByte((byte)(i >> 8));
+ output.WriteByte((byte)i);
+ }
+
+ public static void WriteUint24(int i, byte[] buf, int offset)
+ {
+ buf[offset ] = (byte)(i >> 16);
+ buf[offset + 1] = (byte)(i >> 8);
+ buf[offset + 2] = (byte)i;
+ }
+
+ 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;
+ }
+
+ public static void WriteUint64(long i, Stream output)
+ {
+ 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);
+ }
+
+ public static void WriteUint64(long i, byte[] buf, int offset)
+ {
+ buf[offset ] = (byte)(i >> 56);
+ buf[offset + 1] = (byte)(i >> 48);
+ buf[offset + 2] = (byte)(i >> 40);
+ buf[offset + 3] = (byte)(i >> 32);
+ buf[offset + 4] = (byte)(i >> 24);
+ buf[offset + 5] = (byte)(i >> 16);
+ buf[offset + 6] = (byte)(i >> 8);
+ buf[offset + 7] = (byte)i;
+ }
+
+ public static void WriteOpaque8(byte[] buf, Stream output)
+ {
+ CheckUint8(buf.Length);
+ WriteUint8(buf.Length, output);
+ output.Write(buf, 0, buf.Length);
+ }
+
+ public static void WriteOpaque8(byte[] data, byte[] buf, int off)
+ {
+ CheckUint8(data.Length);
+ WriteUint8(data.Length, buf, off);
+ Array.Copy(data, 0, buf, off + 1, data.Length);
+ }
+
+ public static void WriteOpaque16(byte[] buf, Stream output)
+ {
+ CheckUint16(buf.Length);
+ WriteUint16(buf.Length, output);
+ output.Write(buf, 0, buf.Length);
+ }
+
+ public static void WriteOpaque16(byte[] data, byte[] buf, int off)
+ {
+ CheckUint16(data.Length);
+ WriteUint16(data.Length, buf, off);
+ Array.Copy(data, 0, buf, off + 2, data.Length);
+ }
+
+ public static void WriteOpaque24(byte[] buf, Stream output)
+ {
+ CheckUint24(buf.Length);
+ WriteUint24(buf.Length, output);
+ output.Write(buf, 0, buf.Length);
+ }
+
+ public static void WriteOpaque24(byte[] data, byte[] buf, int off)
+ {
+ CheckUint24(data.Length);
+ WriteUint24(data.Length, buf, off);
+ Array.Copy(data, 0, buf, off + 3, data.Length);
+ }
+
+ public static void WriteUint8Array(short[] u8s, Stream output)
+ {
+ for (int i = 0; i < u8s.Length; ++i)
+ {
+ WriteUint8(u8s[i], output);
+ }
+ }
+
+ public static void WriteUint8Array(short[] u8s, byte[] buf, int offset)
+ {
+ for (int i = 0; i < u8s.Length; ++i)
+ {
+ WriteUint8(u8s[i], buf, offset);
+ ++offset;
+ }
+ }
+
+ public static void WriteUint8ArrayWithUint8Length(short[] u8s, Stream output)
+ {
+ CheckUint8(u8s.Length);
+ WriteUint8(u8s.Length, output);
+ WriteUint8Array(u8s, output);
+ }
+
+ public static void WriteUint8ArrayWithUint8Length(short[] u8s, byte[] buf, int offset)
+ {
+ CheckUint8(u8s.Length);
+ WriteUint8(u8s.Length, buf, offset);
+ WriteUint8Array(u8s, buf, offset + 1);
+ }
+
+ public static void WriteUint16Array(int[] u16s, Stream output)
+ {
+ for (int i = 0; i < u16s.Length; ++i)
+ {
+ WriteUint16(u16s[i], output);
+ }
+ }
+
+ public static void WriteUint16Array(int[] u16s, byte[] buf, int offset)
+ {
+ for (int i = 0; i < u16s.Length; ++i)
+ {
+ WriteUint16(u16s[i], buf, offset);
+ offset += 2;
+ }
+ }
+
+ public static void WriteUint16ArrayWithUint16Length(int[] u16s, Stream output)
+ {
+ int length = 2 * u16s.Length;
+ CheckUint16(length);
+ WriteUint16(length, output);
+ WriteUint16Array(u16s, output);
+ }
+
+ public static void WriteUint16ArrayWithUint16Length(int[] u16s, byte[] buf, int offset)
+ {
+ int length = 2 * u16s.Length;
+ CheckUint16(length);
+ WriteUint16(length, buf, offset);
+ WriteUint16Array(u16s, buf, offset + 2);
+ }
+
+ public static byte[] DecodeOpaque8(byte[] buf)
+ {
+ return DecodeOpaque8(buf, 0);
+ }
+
+ public static byte[] DecodeOpaque8(byte[] buf, int minLength)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (buf.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ short length = ReadUint8(buf, 0);
+ if (buf.Length != (length + 1) || length < minLength)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return CopyOfRangeExact(buf, 1, buf.Length);
+ }
+
+ public static byte[] DecodeOpaque16(byte[] buf)
+ {
+ return DecodeOpaque16(buf, 0);
+ }
+
+ public static byte[] DecodeOpaque16(byte[] buf, int minLength)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (buf.Length < 2)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int length = ReadUint16(buf, 0);
+ if (buf.Length != (length + 2) || length < minLength)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return CopyOfRangeExact(buf, 2, buf.Length);
+ }
+
+ public static short DecodeUint8(byte[] buf)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (buf.Length != 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadUint8(buf, 0);
+ }
+
+ public static short[] DecodeUint8ArrayWithUint8Length(byte[] buf)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+
+ int count = ReadUint8(buf, 0);
+ if (buf.Length != (count + 1))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ short[] uints = new short[count];
+ for (int i = 0; i < count; ++i)
+ {
+ uints[i] = ReadUint8(buf, i + 1);
+ }
+ return uints;
+ }
+
+ public static int DecodeUint16(byte[] buf)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (buf.Length != 2)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadUint16(buf, 0);
+ }
+
+ public static long DecodeUint32(byte[] buf)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (buf.Length != 4)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadUint32(buf, 0);
+ }
+
+ public static byte[] EncodeOpaque8(byte[] buf)
+ {
+ CheckUint8(buf.Length);
+ return Arrays.Prepend(buf, (byte)buf.Length);
+ }
+
+ public static byte[] EncodeOpaque16(byte[] buf)
+ {
+ return Arrays.Concatenate(EncodeUint16(buf.Length), buf);
+ }
+
+ public static byte[] EncodeUint8(short u8)
+ {
+ CheckUint8(u8);
+
+ byte[] encoding = new byte[1];
+ WriteUint8(u8, encoding, 0);
+ return encoding;
+ }
+
+ public static byte[] EncodeUint8ArrayWithUint8Length(short[] u8s)
+ {
+ byte[] result = new byte[1 + u8s.Length];
+ WriteUint8ArrayWithUint8Length(u8s, result, 0);
+ return result;
+ }
+
+ public static byte[] EncodeUint16(int u16)
+ {
+ CheckUint16(u16);
+
+ byte[] encoding = new byte[2];
+ WriteUint16(u16, encoding, 0);
+ return encoding;
+ }
+
+ public static byte[] EncodeUint16ArrayWithUint16Length(int[] u16s)
+ {
+ int length = 2 * u16s.Length;
+ byte[] result = new byte[2 + length];
+ WriteUint16ArrayWithUint16Length(u16s, result, 0);
+ return result;
+ }
+
+ public static byte[] EncodeUint32(long u32)
+ {
+ CheckUint32(u32);
+
+ byte[] encoding = new byte[4];
+ WriteUint32(u32, encoding, 0);
+ return encoding;
+ }
+
+ public static byte[] EncodeVersion(ProtocolVersion version)
+ {
+ return new byte[]{
+ (byte)version.MajorVersion,
+ (byte)version.MinorVersion
+ };
+ }
+
+ public static int ReadInt32(byte[] buf, int offset)
+ {
+ int n = buf[offset] << 24;
+ n |= (buf[++offset] & 0xff) << 16;
+ n |= (buf[++offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return n;
+ }
+
+ public static short ReadUint8(Stream input)
+ {
+ int i = input.ReadByte();
+ if (i < 0)
+ throw new EndOfStreamException();
+ return (short)i;
+ }
+
+ public static short ReadUint8(byte[] buf, int offset)
+ {
+ return (short)(buf[offset] & 0xff);
+ }
+
+ public static int ReadUint16(Stream input)
+ {
+ int i1 = input.ReadByte();
+ int i2 = input.ReadByte();
+ if (i2 < 0)
+ throw new EndOfStreamException();
+ return (i1 << 8) | i2;
+ }
+
+ public static int ReadUint16(byte[] buf, int offset)
+ {
+ int n = (buf[offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return n;
+ }
+
+ public static int ReadUint24(Stream input)
+ {
+ int i1 = input.ReadByte();
+ int i2 = input.ReadByte();
+ int i3 = input.ReadByte();
+ if (i3 < 0)
+ throw new EndOfStreamException();
+
+ return (i1 << 16) | (i2 << 8) | i3;
+ }
+
+ public static int ReadUint24(byte[] buf, int offset)
+ {
+ int n = (buf[offset] & 0xff) << 16;
+ n |= (buf[++offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return 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 ((i1 << 24) | (i2 << 16) | (i3 << 8) | i4) & 0xFFFFFFFFL;
+ }
+
+ public static long ReadUint32(byte[] buf, int offset)
+ {
+ int n = (buf[offset] & 0xff) << 24;
+ n |= (buf[++offset] & 0xff) << 16;
+ n |= (buf[++offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return n & 0xFFFFFFFFL;
+ }
+
+ public static long ReadUint48(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)
+ return EmptyBytes;
+ byte[] buf = new byte[length];
+ if (length != Streams.ReadFully(input, buf))
+ throw new EndOfStreamException();
+ return buf;
+ }
+
+ public static void ReadFully(byte[] buf, Stream input)
+ {
+ int length = buf.Length;
+ if (length > 0 && length != Streams.ReadFully(input, buf))
+ throw new EndOfStreamException();
+ }
+
+ public static byte[] ReadOpaque8(Stream input)
+ {
+ short length = ReadUint8(input);
+ return ReadFully(length, input);
+ }
+
+ public static byte[] ReadOpaque8(Stream input, int minLength)
+ {
+ short length = ReadUint8(input);
+ if (length < minLength)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadFully(length, input);
+ }
+
+ public static byte[] ReadOpaque8(Stream input, int minLength, int maxLength)
+ {
+ short length = ReadUint8(input);
+ if (length < minLength || maxLength < length)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadFully(length, input);
+ }
+
+ public static byte[] ReadOpaque16(Stream input)
+ {
+ int length = ReadUint16(input);
+ return ReadFully(length, input);
+ }
+
+ public static byte[] ReadOpaque16(Stream input, int minLength)
+ {
+ int length = ReadUint16(input);
+ if (length < minLength)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadFully(length, input);
+ }
+
+ public static byte[] ReadOpaque24(Stream input)
+ {
+ int length = ReadUint24(input);
+ return ReadFully(length, input);
+ }
+
+ public static byte[] ReadOpaque24(Stream input, int minLength)
+ {
+ int length = ReadUint24(input);
+ if (length < minLength)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadFully(length, input);
+ }
+
+ public static short[] ReadUint8Array(int count, Stream input)
+ {
+ short[] uints = new short[count];
+ for (int i = 0; i < count; ++i)
+ {
+ uints[i] = ReadUint8(input);
+ }
+ return uints;
+ }
+
+ public static short[] ReadUint8ArrayWithUint8Length(Stream input, int minLength)
+ {
+ int length = ReadUint8(input);
+ if (length < minLength)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return ReadUint8Array(length, input);
+ }
+
+ public static int[] ReadUint16Array(int count, Stream input)
+ {
+ int[] uints = new int[count];
+ for (int i = 0; i < count; ++i)
+ {
+ 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 Asn1Object ReadAsn1Object(byte[] encoding)
+ {
+ Asn1InputStream asn1 = new Asn1InputStream(encoding);
+ Asn1Object result = asn1.ReadObject();
+ if (null == result)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ if (null != asn1.ReadObject())
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return result;
+ }
+
+ public static Asn1Object ReadDerObject(byte[] encoding)
+ {
+ /*
+ * NOTE: The current ASN.1 parsing code can't enforce DER-only parsing, but since DER is
+ * canonical, we can check it by re-encoding the result and comparing to the original.
+ */
+ Asn1Object result = ReadAsn1Object(encoding);
+ byte[] check = result.GetEncoded(Asn1Encodable.Der);
+ if (!Arrays.AreEqual(check, encoding))
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return result;
+ }
+
+ public static void WriteGmtUnixTime(byte[] buf, int offset)
+ {
+ int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L);
+ buf[offset ] = (byte)(t >> 24);
+ buf[offset + 1] = (byte)(t >> 16);
+ buf[offset + 2] = (byte)(t >> 8);
+ buf[offset + 3] = (byte)t;
+ }
+
+ 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 void AddIfSupported(IList supportedAlgs, TlsCrypto crypto, SignatureAndHashAlgorithm alg)
+ {
+ if (crypto.HasSignatureAndHashAlgorithm(alg))
+ {
+ supportedAlgs.Add(alg);
+ }
+ }
+
+ public static void AddIfSupported(IList supportedGroups, TlsCrypto crypto, int namedGroup)
+ {
+ if (crypto.HasNamedGroup(namedGroup))
+ {
+ supportedGroups.Add(namedGroup);
+ }
+ }
+
+ public static void AddIfSupported(IList supportedGroups, TlsCrypto crypto, int[] namedGroups)
+ {
+ for (int i = 0; i < namedGroups.Length; ++i)
+ {
+ AddIfSupported(supportedGroups, crypto, namedGroups[i]);
+ }
+ }
+
+ public static bool AddToSet(IList s, int i)
+ {
+ bool result = !s.Contains(i);
+ if (result)
+ {
+ s.Add(i);
+ }
+ return result;
+ }
+
+ public static IList GetDefaultDssSignatureAlgorithms()
+ {
+ return GetDefaultSignatureAlgorithms(SignatureAlgorithm.dsa);
+ }
+
+ public static IList GetDefaultECDsaSignatureAlgorithms()
+ {
+ return GetDefaultSignatureAlgorithms(SignatureAlgorithm.ecdsa);
+ }
+
+ public static IList GetDefaultRsaSignatureAlgorithms()
+ {
+ return GetDefaultSignatureAlgorithms(SignatureAlgorithm.rsa);
+ }
+
+ public static SignatureAndHashAlgorithm GetDefaultSignatureAlgorithm(short signatureAlgorithm)
+ {
+ /*
+ * RFC 5246 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}.
+ */
+
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.dsa:
+ case SignatureAlgorithm.ecdsa:
+ case SignatureAlgorithm.rsa:
+ return SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, signatureAlgorithm);
+ default:
+ return null;
+ }
+ }
+
+ public static IList GetDefaultSignatureAlgorithms(short signatureAlgorithm)
+ {
+ SignatureAndHashAlgorithm sigAndHashAlg = GetDefaultSignatureAlgorithm(signatureAlgorithm);
+
+ return null == sigAndHashAlg ? Platform.CreateArrayList() : VectorOfOne(sigAndHashAlg);
+ }
+
+ public static IList GetDefaultSupportedSignatureAlgorithms(TlsContext context)
+ {
+ TlsCrypto crypto = context.Crypto;
+
+ IList result = Platform.CreateArrayList(DefaultSupportedSigAlgs.Count);
+ foreach (SignatureAndHashAlgorithm sigAndHashAlg in DefaultSupportedSigAlgs)
+ {
+ AddIfSupported(result, crypto, sigAndHashAlg);
+ }
+ return result;
+ }
+
+ public static SignatureAndHashAlgorithm GetSignatureAndHashAlgorithm(TlsContext context,
+ TlsCredentialedSigner signerCredentials)
+ {
+ return GetSignatureAndHashAlgorithm(context.ServerVersion, signerCredentials);
+ }
+
+ internal static SignatureAndHashAlgorithm GetSignatureAndHashAlgorithm(ProtocolVersion negotiatedVersion,
+ TlsCredentialedSigner signerCredentials)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ if (IsTlsV12(negotiatedVersion))
+ {
+ signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm;
+ if (signatureAndHashAlgorithm == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ return signatureAndHashAlgorithm;
+ }
+
+ public static byte[] GetExtensionData(IDictionary extensions, int extensionType)
+ {
+ return extensions == null || !extensions.Contains(extensionType)
+ ? null
+ : (byte[])extensions[extensionType];
+ }
+
+ public static bool HasExpectedEmptyExtensionData(IDictionary extensions, int extensionType,
+ short 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);
+ }
+
+ internal static bool IsExtendedMasterSecretOptionalDtls(ProtocolVersion[] activeProtocolVersions)
+ {
+ return ProtocolVersion.Contains(activeProtocolVersions, ProtocolVersion.DTLSv12)
+ || ProtocolVersion.Contains(activeProtocolVersions, ProtocolVersion.DTLSv10);
+ }
+
+ internal static bool IsExtendedMasterSecretOptionalTls(ProtocolVersion[] activeProtocolVersions)
+ {
+ return ProtocolVersion.Contains(activeProtocolVersions, ProtocolVersion.TLSv12)
+ || ProtocolVersion.Contains(activeProtocolVersions, ProtocolVersion.TLSv11)
+ || ProtocolVersion.Contains(activeProtocolVersions, ProtocolVersion.TLSv10);
+ }
+
+ public static bool IsNullOrContainsNull(object[] array)
+ {
+ if (null == array)
+ return true;
+
+ int count = array.Length;
+ for (int i = 0; i < count; ++i)
+ {
+ if (null == array[i])
+ return true;
+ }
+ return false;
+ }
+
+ public static bool IsNullOrEmpty(byte[] array)
+ {
+ return null == array || array.Length < 1;
+ }
+
+ public static bool IsNullOrEmpty(short[] array)
+ {
+ return null == array || array.Length < 1;
+ }
+
+ public static bool IsNullOrEmpty(int[] array)
+ {
+ return null == array || array.Length < 1;
+ }
+
+ public static bool IsNullOrEmpty(object[] array)
+ {
+ return null == array || array.Length < 1;
+ }
+
+ public static bool IsNullOrEmpty(string s)
+ {
+ return null == s || s.Length < 1;
+ }
+
+ public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion version)
+ {
+ return null != version
+ && ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static short GetLegacyClientCertType(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ return ClientCertificateType.rsa_sign;
+ case SignatureAlgorithm.dsa:
+ return ClientCertificateType.dss_sign;
+ case SignatureAlgorithm.ecdsa:
+ return ClientCertificateType.ecdsa_sign;
+ default:
+ return -1;
+ }
+ }
+
+ public static short GetLegacySignatureAlgorithmClient(short clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ return SignatureAlgorithm.dsa;
+ case ClientCertificateType.ecdsa_sign:
+ return SignatureAlgorithm.ecdsa;
+ case ClientCertificateType.rsa_sign:
+ return SignatureAlgorithm.rsa;
+ default:
+ return -1;
+ }
+ }
+
+ public static short GetLegacySignatureAlgorithmClientCert(short clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.dss_fixed_dh:
+ return SignatureAlgorithm.dsa;
+
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.ecdsa_fixed_ecdh:
+ return SignatureAlgorithm.ecdsa;
+
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.rsa_fixed_dh:
+ case ClientCertificateType.rsa_fixed_ecdh:
+ return SignatureAlgorithm.rsa;
+ default:
+ return -1;
+ }
+ }
+
+ public static short GetLegacySignatureAlgorithmServer(int keyExchangeAlgorithm)
+ {
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ return SignatureAlgorithm.dsa;
+
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ return SignatureAlgorithm.ecdsa;
+
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return SignatureAlgorithm.rsa;
+
+ default:
+ return -1;
+ }
+ }
+
+ public static short GetLegacySignatureAlgorithmServerCert(int keyExchangeAlgorithm)
+ {
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ return SignatureAlgorithm.dsa;
+
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ return SignatureAlgorithm.ecdsa;
+
+ 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:
+ return SignatureAlgorithm.rsa;
+
+ default:
+ return -1;
+ }
+ }
+
+ public static IList GetLegacySupportedSignatureAlgorithms()
+ {
+ IList result = Platform.CreateArrayList(3);
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, SignatureAlgorithm.dsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa));
+ result.Add(SignatureAndHashAlgorithm.GetInstance(HashAlgorithm.sha1, SignatureAlgorithm.rsa));
+ return result;
+ }
+
+ /// <exception cref="IOException"/>
+ public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, Stream output)
+ {
+ if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.Count < 1
+ || supportedSignatureAlgorithms.Count >= (1 << 15))
+ {
+ throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms");
+ }
+
+ // supported_signature_algorithms
+ int length = 2 * supportedSignatureAlgorithms.Count;
+ CheckUint16(length);
+ WriteUint16(length, output);
+ foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
+ {
+ if (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 ArgumentException(
+ "SignatureAlgorithm.anonymous MUST NOT appear in the signature_algorithms extension");
+ }
+ entry.Encode(output);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public static IList ParseSupportedSignatureAlgorithms(Stream input)
+ {
+ // 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)
+ {
+ SignatureAndHashAlgorithm sigAndHashAlg = SignatureAndHashAlgorithm.Parse(input);
+
+ if (SignatureAlgorithm.anonymous != sigAndHashAlg.Signature)
+ {
+ supportedSignatureAlgorithms.Add(sigAndHashAlg);
+ }
+ }
+ return supportedSignatureAlgorithms;
+ }
+
+ /// <exception cref="IOException"/>
+ public static void VerifySupportedSignatureAlgorithm(IList supportedSignatureAlgorithms,
+ SignatureAndHashAlgorithm signatureAlgorithm)
+ {
+ if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.Count < 1
+ || supportedSignatureAlgorithms.Count >= (1 << 15))
+ {
+ throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms");
+ }
+ if (signatureAlgorithm == null)
+ throw new ArgumentNullException("signatureAlgorithm");
+
+ if (signatureAlgorithm.Signature == SignatureAlgorithm.anonymous
+ || !ContainsSignatureAlgorithm(supportedSignatureAlgorithms, signatureAlgorithm))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool ContainsSignatureAlgorithm(IList supportedSignatureAlgorithms,
+ SignatureAndHashAlgorithm signatureAlgorithm)
+ {
+ foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
+ {
+ if (entry.Equals(signatureAlgorithm))
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool ContainsAnySignatureAlgorithm(IList supportedSignatureAlgorithms, short signatureAlgorithm)
+ {
+ foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
+ {
+ if (entry.Signature == signatureAlgorithm)
+ return true;
+ }
+
+ return false;
+ }
+
+ public static TlsSecret Prf(SecurityParameters securityParameters, TlsSecret secret, string asciiLabel,
+ byte[] seed, int length)
+ {
+ return secret.DeriveUsingPrf(securityParameters.PrfAlgorithm, asciiLabel, seed, length);
+ }
+
+ public static byte[] Clone(byte[] data)
+ {
+ return null == data ? null : data.Length == 0 ? EmptyBytes : (byte[])data.Clone();
+ }
+
+ public static string[] Clone(string[] s)
+ {
+ return null == s ? null : s.Length < 1 ? EmptyStrings : (string[])s.Clone();
+ }
+
+ public static bool ConstantTimeAreEqual(int len, byte[] a, int aOff, byte[] b, int bOff)
+ {
+ int d = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ d |= a[aOff + i] ^ b[bOff + i];
+ }
+ return 0 == d;
+ }
+
+ public static byte[] CopyOfRangeExact(byte[] original, int from, int to)
+ {
+ int newLength = to - from;
+ byte[] copy = new byte[newLength];
+ Array.Copy(original, from, copy, 0, newLength);
+ return copy;
+ }
+
+ internal static byte[] Concat(byte[] a, byte[] b)
+ {
+ byte[] c = new byte[a.Length + b.Length];
+ Array.Copy(a, 0, c, 0, a.Length);
+ Array.Copy(b, 0, c, a.Length, b.Length);
+ return c;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] CalculateEndPointHash(TlsContext context, TlsCertificate certificate, byte[] enc)
+ {
+ return CalculateEndPointHash(context, certificate, enc, 0, enc.Length);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static byte[] CalculateEndPointHash(TlsContext context, TlsCertificate certificate, byte[] enc,
+ int encOff, int encLen)
+ {
+ short hashAlgorithm = HashAlgorithm.none;
+
+ string sigAlgOid = certificate.SigAlgOid;
+ if (sigAlgOid != null)
+ {
+ if (PkcsObjectIdentifiers.IdRsassaPss.Id.Equals(sigAlgOid))
+ {
+ RsassaPssParameters pssParams = RsassaPssParameters.GetInstance(certificate.GetSigAlgParams());
+ if (null != pssParams)
+ {
+ DerObjectIdentifier hashOid = pssParams.HashAlgorithm.Algorithm;
+ if (NistObjectIdentifiers.IdSha256.Equals(hashOid))
+ {
+ hashAlgorithm = HashAlgorithm.sha256;
+ }
+ else if (NistObjectIdentifiers.IdSha384.Equals(hashOid))
+ {
+ hashAlgorithm = HashAlgorithm.sha384;
+ }
+ else if (NistObjectIdentifiers.IdSha512.Equals(hashOid))
+ {
+ hashAlgorithm = HashAlgorithm.sha512;
+ }
+ }
+ }
+ else
+ {
+ if (CertSigAlgOids.Contains(sigAlgOid))
+ {
+ hashAlgorithm = ((SignatureAndHashAlgorithm)CertSigAlgOids[sigAlgOid]).Hash;
+ }
+ }
+ }
+
+ switch (hashAlgorithm)
+ {
+ case HashAlgorithm.Intrinsic:
+ hashAlgorithm = HashAlgorithm.none;
+ break;
+ case HashAlgorithm.md5:
+ case HashAlgorithm.sha1:
+ hashAlgorithm = HashAlgorithm.sha256;
+ break;
+ }
+
+ if (HashAlgorithm.none != hashAlgorithm)
+ {
+ TlsHash hash = CreateHash(context.Crypto, hashAlgorithm);
+ if (hash != null)
+ {
+ hash.Update(enc, encOff, encLen);
+ return hash.CalculateHash();
+ }
+ }
+
+ return EmptyBytes;
+ }
+
+ public static byte[] CalculateExporterSeed(SecurityParameters securityParameters, byte[] context)
+ {
+ byte[] cr = securityParameters.ClientRandom, sr = securityParameters.ServerRandom;
+ if (null == context)
+ return Arrays.Concatenate(cr, sr);
+
+ if (!IsValidUint16(context.Length))
+ throw new ArgumentException("must have length less than 2^16 (or be null)", "context");
+
+ byte[] contextLength = new byte[2];
+ WriteUint16(context.Length, contextLength, 0);
+
+ return Arrays.ConcatenateAll(cr, sr, contextLength, context);
+ }
+
+ internal static TlsSecret CalculateMasterSecret(TlsContext context, TlsSecret preMasterSecret)
+ {
+ SecurityParameters sp = context.SecurityParameters;
+
+ string asciiLabel;
+ byte[] seed;
+ if (sp.IsExtendedMasterSecret)
+ {
+ asciiLabel = ExporterLabel.extended_master_secret;
+ seed = sp.SessionHash;
+ }
+ else
+ {
+ asciiLabel = ExporterLabel.master_secret;
+ seed = Concat(sp.ClientRandom, sp.ServerRandom);
+ }
+
+ return Prf(sp, preMasterSecret, asciiLabel, seed, 48);
+ }
+
+ internal static byte[] CalculateVerifyData(TlsContext context, TlsHandshakeHash handshakeHash, bool isServer)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (IsTlsV13(negotiatedVersion))
+ {
+ TlsSecret baseKey = isServer
+ ? securityParameters.BaseKeyServer
+ : securityParameters.BaseKeyClient;
+
+ TlsSecret finishedKey = DeriveSecret(securityParameters, baseKey, "finished", EmptyBytes);
+ byte[] transcriptHash = GetCurrentPrfHash(handshakeHash);
+
+ TlsCrypto crypto = context.Crypto;
+ byte[] hmacKey = crypto.AdoptSecret(finishedKey).Extract();
+ TlsHmac hmac = crypto.CreateHmacForHash(TlsCryptoUtilities.GetHash(securityParameters.PrfHashAlgorithm));
+ hmac.SetKey(hmacKey, 0, hmacKey.Length);
+ hmac.Update(transcriptHash, 0, transcriptHash.Length);
+ return hmac.CalculateMac();
+ }
+
+ if (negotiatedVersion.IsSsl)
+ {
+ return Ssl3Utilities.CalculateVerifyData(handshakeHash, isServer);
+ }
+
+ string asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished;
+ byte[] prfHash = GetCurrentPrfHash(handshakeHash);
+
+ TlsSecret master_secret = securityParameters.MasterSecret;
+ int verify_data_length = securityParameters.VerifyDataLength;
+
+ return Prf(securityParameters, master_secret, asciiLabel, prfHash, verify_data_length).Extract();
+ }
+
+ internal static void Establish13PhaseSecrets(TlsContext context)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(securityParameters.PrfHashAlgorithm);
+ int hashLen = securityParameters.PrfHashLength;
+ byte[] zeroes = new byte[hashLen];
+
+ byte[] psk = securityParameters.Psk;
+ if (null == psk)
+ {
+ psk = zeroes;
+ }
+ else
+ {
+ securityParameters.m_psk = null;
+ }
+
+ byte[] ecdhe = zeroes;
+ TlsSecret sharedSecret = securityParameters.SharedSecret;
+ if (null != sharedSecret)
+ {
+ securityParameters.m_sharedSecret = null;
+ ecdhe = sharedSecret.Extract();
+ }
+
+ TlsCrypto crypto = context.Crypto;
+
+ byte[] emptyTranscriptHash = crypto.CreateHash(cryptoHashAlgorithm).CalculateHash();
+
+ TlsSecret earlySecret = crypto.HkdfInit(cryptoHashAlgorithm)
+ .HkdfExtract(cryptoHashAlgorithm, psk);
+ TlsSecret handshakeSecret = DeriveSecret(securityParameters, earlySecret, "derived", emptyTranscriptHash)
+ .HkdfExtract(cryptoHashAlgorithm, ecdhe);
+ TlsSecret masterSecret = DeriveSecret(securityParameters, handshakeSecret, "derived", emptyTranscriptHash)
+ .HkdfExtract(cryptoHashAlgorithm, zeroes);
+
+ securityParameters.m_earlySecret = earlySecret;
+ securityParameters.m_handshakeSecret = handshakeSecret;
+ securityParameters.m_masterSecret = masterSecret;
+ }
+
+ private static void Establish13TrafficSecrets(TlsContext context, byte[] transcriptHash, TlsSecret phaseSecret,
+ string clientLabel, string serverLabel, RecordStream recordStream)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ securityParameters.m_trafficSecretClient = DeriveSecret(securityParameters, phaseSecret, clientLabel,
+ transcriptHash);
+
+ if (null != serverLabel)
+ {
+ securityParameters.m_trafficSecretServer = DeriveSecret(securityParameters, phaseSecret, serverLabel,
+ transcriptHash);
+ }
+
+ // TODO[tls13] Early data (client->server only)
+
+ recordStream.SetPendingCipher(InitCipher(context));
+ }
+
+ internal static void Establish13PhaseApplication(TlsContext context, byte[] serverFinishedTranscriptHash,
+ RecordStream recordStream)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ TlsSecret phaseSecret = securityParameters.MasterSecret;
+
+ Establish13TrafficSecrets(context, serverFinishedTranscriptHash, phaseSecret, "c ap traffic",
+ "s ap traffic", recordStream);
+
+ securityParameters.m_exporterMasterSecret = DeriveSecret(securityParameters, phaseSecret, "exp master",
+ serverFinishedTranscriptHash);
+ }
+
+ internal static void Establish13PhaseEarly(TlsContext context, byte[] clientHelloTranscriptHash,
+ RecordStream recordStream)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ TlsSecret phaseSecret = securityParameters.EarlySecret;
+
+ // TODO[tls13] binder_key
+
+ // TODO[tls13] Early data (client->server only)
+ if (null != recordStream)
+ {
+ Establish13TrafficSecrets(context, clientHelloTranscriptHash, phaseSecret, "c e traffic", null,
+ recordStream);
+ }
+
+ securityParameters.m_earlyExporterMasterSecret = DeriveSecret(securityParameters, phaseSecret,
+ "e exp master", clientHelloTranscriptHash);
+ }
+
+ internal static void Establish13PhaseHandshake(TlsContext context, byte[] serverHelloTranscriptHash,
+ RecordStream recordStream)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ TlsSecret phaseSecret = securityParameters.HandshakeSecret;
+
+ Establish13TrafficSecrets(context, serverHelloTranscriptHash, phaseSecret, "c hs traffic", "s hs traffic",
+ recordStream);
+
+ securityParameters.m_baseKeyClient = securityParameters.TrafficSecretClient;
+ securityParameters.m_baseKeyServer = securityParameters.TrafficSecretServer;
+ }
+
+ internal static void Update13TrafficSecretLocal(TlsContext context)
+ {
+ Update13TrafficSecret(context, context.IsServer);
+ }
+
+ internal static void Update13TrafficSecretPeer(TlsContext context)
+ {
+ Update13TrafficSecret(context, !context.IsServer);
+ }
+
+ private static void Update13TrafficSecret(TlsContext context, bool forServer)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ TlsSecret current;
+ if (forServer)
+ {
+ current = securityParameters.TrafficSecretServer;
+ securityParameters.m_trafficSecretServer = Update13TrafficSecret(securityParameters, current);
+ }
+ else
+ {
+ current = securityParameters.TrafficSecretClient;
+ securityParameters.m_trafficSecretClient = Update13TrafficSecret(securityParameters, current);
+ }
+
+ if (null != current)
+ {
+ current.Destroy();
+ }
+ }
+
+ private static TlsSecret Update13TrafficSecret(SecurityParameters securityParameters, TlsSecret secret)
+ {
+ return TlsCryptoUtilities.HkdfExpandLabel(secret, securityParameters.PrfHashAlgorithm, "traffic upd",
+ EmptyBytes, securityParameters.PrfHashLength);
+ }
+
+ public static short GetHashAlgorithmForPrfAlgorithm(int prfAlgorithm)
+ {
+ switch (prfAlgorithm)
+ {
+ case PrfAlgorithm.ssl_prf_legacy:
+ case PrfAlgorithm.tls_prf_legacy:
+ throw new ArgumentException("legacy PRF not a valid algorithm");
+ case PrfAlgorithm.tls_prf_sha256:
+ case PrfAlgorithm.tls13_hkdf_sha256:
+ return HashAlgorithm.sha256;
+ case PrfAlgorithm.tls_prf_sha384:
+ case PrfAlgorithm.tls13_hkdf_sha384:
+ return HashAlgorithm.sha384;
+ // TODO[RFC 8998]
+ //case PrfAlgorithm.tls13_hkdf_sm3:
+ // return HashAlgorithm.sm3;
+ default:
+ throw new ArgumentException("unknown PrfAlgorithm: " + PrfAlgorithm.GetText(prfAlgorithm));
+ }
+ }
+
+ public static DerObjectIdentifier GetOidForHashAlgorithm(short 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;
+ // TODO[RFC 8998]
+ //case HashAlgorithm.sm3:
+ // return GMObjectIdentifiers.sm3;
+ default:
+ throw new ArgumentException("invalid HashAlgorithm: " + HashAlgorithm.GetText(hashAlgorithm));
+ }
+ }
+
+ internal static int GetPrfAlgorithm(SecurityParameters securityParameters, int cipherSuite)
+ {
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ bool isTlsV13 = IsTlsV13(negotiatedVersion);
+ bool isTlsV12Exactly = !isTlsV13 && IsTlsV12(negotiatedVersion);
+ bool isSsl = negotiatedVersion.IsSsl;
+
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
+ {
+ if (isTlsV13)
+ return PrfAlgorithm.tls13_hkdf_sha256;
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_AES_256_GCM_SHA384:
+ {
+ if (isTlsV13)
+ return PrfAlgorithm.tls13_hkdf_sha384;
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_SM4_CCM_SM3:
+ case CipherSuite.TLS_SM4_GCM_SM3:
+ {
+ if (isTlsV13)
+ return PrfAlgorithm.tls13_hkdf_sm3;
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_GCM_SHA256:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_128_GCM_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_128_GCM_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_GCM_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_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_PSK_WITH_CHACHA20_POLY1305_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ 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_PSK_WITH_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_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_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_128_GCM_SHA256:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CHACHA20_POLY1305_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_GCM_SHA256:
+ 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 (isTlsV12Exactly)
+ return PrfAlgorithm.tls_prf_sha256;
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_GCM_SHA384:
+ 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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_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_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_256_CBC_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ {
+ if (isTlsV12Exactly)
+ 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 (isTlsV13)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ if (isTlsV12Exactly)
+ return PrfAlgorithm.tls_prf_sha384;
+
+ if (isSsl)
+ return PrfAlgorithm.ssl_prf_legacy;
+
+ return PrfAlgorithm.tls_prf_legacy;
+ }
+
+ default:
+ {
+ if (isTlsV13)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ if (isTlsV12Exactly)
+ return PrfAlgorithm.tls_prf_sha256;
+
+ if (isSsl)
+ return PrfAlgorithm.ssl_prf_legacy;
+
+ return PrfAlgorithm.tls_prf_legacy;
+ }
+ }
+ }
+
+ internal static byte[] CalculateSignatureHash(TlsContext context, SignatureAndHashAlgorithm algorithm,
+ DigestInputBuffer buf)
+ {
+ TlsCrypto crypto = context.Crypto;
+
+ TlsHash h = algorithm == null
+ ? new CombinedHash(crypto)
+ : CreateHash(crypto, algorithm.Hash);
+
+ SecurityParameters sp = context.SecurityParameters;
+ byte[] cr = sp.ClientRandom, sr = sp.ServerRandom;
+ h.Update(cr, 0, cr.Length);
+ h.Update(sr, 0, sr.Length);
+ buf.UpdateDigest(h);
+
+ return h.CalculateHash();
+ }
+
+ internal static void SendSignatureInput(TlsContext context, DigestInputBuffer buf, Stream output)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ // NOTE: The implicit copy here is intended (and important)
+ byte[] randoms = Arrays.Concatenate(securityParameters.ClientRandom, securityParameters.ServerRandom);
+ output.Write(randoms, 0, randoms.Length);
+ buf.CopyTo(output);
+ Platform.Dispose(output);
+ }
+
+ internal static DigitallySigned GenerateCertificateVerifyClient(TlsClientContext clientContext,
+ TlsCredentialedSigner credentialedSigner, TlsStreamSigner streamSigner, TlsHandshakeHash handshakeHash)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (IsTlsV13(negotiatedVersion))
+ {
+ // Should be using GenerateCertificateVerify13 instead
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = GetSignatureAndHashAlgorithm(negotiatedVersion,
+ credentialedSigner);
+
+ byte[] signature;
+ if (streamSigner != null)
+ {
+ handshakeHash.CopyBufferTo(streamSigner.GetOutputStream());
+ signature = streamSigner.GetSignature();
+ }
+ else
+ {
+ byte[] hash;
+ if (signatureAndHashAlgorithm == null)
+ {
+ hash = securityParameters.SessionHash;
+ }
+ else
+ {
+ int signatureScheme = SignatureScheme.From(signatureAndHashAlgorithm);
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ hash = handshakeHash.GetFinalHash(cryptoHashAlgorithm);
+ }
+
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ return new DigitallySigned(signatureAndHashAlgorithm, signature);
+ }
+
+ internal static DigitallySigned Generate13CertificateVerify(TlsContext context,
+ TlsCredentialedSigner credentialedSigner, TlsHandshakeHash handshakeHash)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = credentialedSigner.SignatureAndHashAlgorithm;
+ if (null == signatureAndHashAlgorithm)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ string contextString = context.IsServer
+ ? "TLS 1.3, server CertificateVerify"
+ : "TLS 1.3, client CertificateVerify";
+
+ byte[] signature = Generate13CertificateVerify(context.Crypto, credentialedSigner, contextString,
+ handshakeHash, signatureAndHashAlgorithm);
+
+ return new DigitallySigned(signatureAndHashAlgorithm, signature);
+ }
+
+ private static byte[] Generate13CertificateVerify(TlsCrypto crypto, TlsCredentialedSigner credentialedSigner,
+ string contextString, TlsHandshakeHash handshakeHash, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ TlsStreamSigner streamSigner = credentialedSigner.GetStreamSigner();
+
+ byte[] header = GetCertificateVerifyHeader(contextString);
+ byte[] prfHash = GetCurrentPrfHash(handshakeHash);
+
+ if (null != streamSigner)
+ {
+ Stream output = streamSigner.GetOutputStream();
+ output.Write(header, 0, header.Length);
+ output.Write(prfHash, 0, prfHash.Length);
+ return streamSigner.GetSignature();
+ }
+
+ int signatureScheme = SignatureScheme.From(signatureAndHashAlgorithm);
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ TlsHash tlsHash = crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(header, 0, header.Length);
+ tlsHash.Update(prfHash, 0, prfHash.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ return credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ internal static void VerifyCertificateVerifyClient(TlsServerContext serverContext,
+ CertificateRequest certificateRequest, DigitallySigned certificateVerify, TlsHandshakeHash handshakeHash)
+ {
+ SecurityParameters securityParameters = serverContext.SecurityParameters;
+ Certificate clientCertificate = securityParameters.PeerCertificate;
+ TlsCertificate verifyingCert = clientCertificate.GetCertificateAt(0);
+ SignatureAndHashAlgorithm sigAndHashAlg = certificateVerify.Algorithm;
+ short signatureAlgorithm;
+
+ if (null == sigAndHashAlg)
+ {
+ signatureAlgorithm = verifyingCert.GetLegacySignatureAlgorithm();
+
+ short clientCertType = GetLegacyClientCertType(signatureAlgorithm);
+ if (clientCertType < 0 || !Arrays.Contains(certificateRequest.CertificateTypes, clientCertType))
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+ else
+ {
+ signatureAlgorithm = sigAndHashAlg.Signature;
+
+ // TODO Is it possible (maybe only pre-1.2 to check this immediately when the Certificate arrives?
+ if (!IsValidSignatureAlgorithmForCertificateVerify(signatureAlgorithm,
+ certificateRequest.CertificateTypes))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ VerifySupportedSignatureAlgorithm(securityParameters.ServerSigAlgs, sigAndHashAlg);
+ }
+
+ // Verify the CertificateVerify message contains a correct signature.
+ bool verified;
+ try
+ {
+ TlsVerifier verifier = verifyingCert.CreateVerifier(signatureAlgorithm);
+ TlsStreamVerifier streamVerifier = verifier.GetStreamVerifier(certificateVerify);
+
+ if (streamVerifier != null)
+ {
+ handshakeHash.CopyBufferTo(streamVerifier.GetOutputStream());
+ verified = streamVerifier.IsVerified();
+ }
+ else
+ {
+ byte[] hash;
+ if (IsTlsV12(serverContext))
+ {
+ int signatureScheme = SignatureScheme.From(sigAndHashAlg);
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ hash = handshakeHash.GetFinalHash(cryptoHashAlgorithm);
+ }
+ else
+ {
+ hash = securityParameters.SessionHash;
+ }
+
+ verified = verifier.VerifyRawSignature(certificateVerify, hash);
+ }
+ }
+ catch (TlsFatalAlert e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error, e);
+ }
+
+ if (!verified)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ internal static void Verify13CertificateVerifyClient(TlsServerContext serverContext,
+ CertificateRequest certificateRequest, DigitallySigned certificateVerify, TlsHandshakeHash handshakeHash)
+ {
+ SecurityParameters securityParameters = serverContext.SecurityParameters;
+ Certificate clientCertificate = securityParameters.PeerCertificate;
+ TlsCertificate verifyingCert = clientCertificate.GetCertificateAt(0);
+
+ SignatureAndHashAlgorithm sigAndHashAlg = certificateVerify.Algorithm;
+ VerifySupportedSignatureAlgorithm(securityParameters.ServerSigAlgs, sigAndHashAlg);
+
+ int signatureScheme = SignatureScheme.From(sigAndHashAlg);
+
+ // Verify the CertificateVerify message contains a correct signature.
+ bool verified;
+ try
+ {
+ TlsVerifier verifier = verifyingCert.CreateVerifier(signatureScheme);
+
+ verified = Verify13CertificateVerify(serverContext.Crypto, certificateVerify, verifier,
+ "TLS 1.3, client CertificateVerify", handshakeHash);
+ }
+ catch (TlsFatalAlert e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error, e);
+ }
+
+ if (!verified)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ internal static void Verify13CertificateVerifyServer(TlsClientContext clientContext,
+ DigitallySigned certificateVerify, TlsHandshakeHash handshakeHash)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ Certificate serverCertificate = securityParameters.PeerCertificate;
+ TlsCertificate verifyingCert = serverCertificate.GetCertificateAt(0);
+
+ SignatureAndHashAlgorithm sigAndHashAlg = certificateVerify.Algorithm;
+ VerifySupportedSignatureAlgorithm(securityParameters.ClientSigAlgs, sigAndHashAlg);
+
+ int signatureScheme = SignatureScheme.From(sigAndHashAlg);
+
+ // Verify the CertificateVerify message contains a correct signature.
+ bool verified;
+ try
+ {
+ TlsVerifier verifier = verifyingCert.CreateVerifier(signatureScheme);
+
+ verified = Verify13CertificateVerify(clientContext.Crypto, certificateVerify, verifier,
+ "TLS 1.3, server CertificateVerify", handshakeHash);
+ }
+ catch (TlsFatalAlert e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error, e);
+ }
+
+ if (!verified)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ private static bool Verify13CertificateVerify(TlsCrypto crypto, DigitallySigned certificateVerify,
+ TlsVerifier verifier, string contextString, TlsHandshakeHash handshakeHash)
+ {
+ TlsStreamVerifier streamVerifier = verifier.GetStreamVerifier(certificateVerify);
+
+ byte[] header = GetCertificateVerifyHeader(contextString);
+ byte[] prfHash = GetCurrentPrfHash(handshakeHash);
+
+ if (null != streamVerifier)
+ {
+ Stream output = streamVerifier.GetOutputStream();
+ output.Write(header, 0, header.Length);
+ output.Write(prfHash, 0, prfHash.Length);
+ return streamVerifier.IsVerified();
+ }
+
+ int signatureScheme = SignatureScheme.From(certificateVerify.Algorithm);
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ TlsHash tlsHash = crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(header, 0, header.Length);
+ tlsHash.Update(prfHash, 0, prfHash.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ return verifier.VerifyRawSignature(certificateVerify, hash);
+ }
+
+ private static byte[] GetCertificateVerifyHeader(string contextString)
+ {
+ int count = contextString.Length;
+ byte[] header = new byte[64 + count + 1];
+ for (int i = 0; i < 64; ++i)
+ {
+ header[i] = 0x20;
+ }
+ for (int i = 0; i < count; ++i)
+ {
+ char c = contextString[i];
+ header[64 + i] = (byte)c;
+ }
+ header[64 + count] = 0x00;
+ return header;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void GenerateServerKeyExchangeSignature(TlsContext context, TlsCredentialedSigner credentials,
+ DigestInputBuffer digestBuffer)
+ {
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm algorithm = GetSignatureAndHashAlgorithm(context, credentials);
+ TlsStreamSigner streamSigner = credentials.GetStreamSigner();
+
+ byte[] signature;
+ if (streamSigner != null)
+ {
+ SendSignatureInput(context, digestBuffer, streamSigner.GetOutputStream());
+ signature = streamSigner.GetSignature();
+ }
+ else
+ {
+ byte[] hash = CalculateSignatureHash(context, algorithm, digestBuffer);
+ signature = credentials.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(algorithm, signature);
+
+ digitallySigned.Encode(digestBuffer);
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void VerifyServerKeyExchangeSignature(TlsContext context, Stream signatureInput,
+ TlsCertificate serverCertificate, DigestInputBuffer digestBuffer)
+ {
+ DigitallySigned digitallySigned = DigitallySigned.Parse(context, signatureInput);
+
+ SecurityParameters securityParameters = context.SecurityParameters;
+ int keyExchangeAlgorithm = securityParameters.KeyExchangeAlgorithm;
+
+ SignatureAndHashAlgorithm sigAndHashAlg = digitallySigned.Algorithm;
+ short signatureAlgorithm;
+
+ if (sigAndHashAlg == null)
+ {
+ signatureAlgorithm = GetLegacySignatureAlgorithmServer(keyExchangeAlgorithm);
+ }
+ else
+ {
+ signatureAlgorithm = sigAndHashAlg.Signature;
+
+ if (!IsValidSignatureAlgorithmForServerKeyExchange(signatureAlgorithm, keyExchangeAlgorithm))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ VerifySupportedSignatureAlgorithm(securityParameters.ClientSigAlgs, sigAndHashAlg);
+ }
+
+ TlsVerifier verifier = serverCertificate.CreateVerifier(signatureAlgorithm);
+ TlsStreamVerifier streamVerifier = verifier.GetStreamVerifier(digitallySigned);
+
+ bool verified;
+ if (streamVerifier != null)
+ {
+ SendSignatureInput(context, digestBuffer, streamVerifier.GetOutputStream());
+ verified = streamVerifier.IsVerified();
+ }
+ else
+ {
+ byte[] hash = CalculateSignatureHash(context, sigAndHashAlg, digestBuffer);
+ verified = verifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ if (!verified)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ internal static void TrackHashAlgorithms(TlsHandshakeHash handshakeHash, IList supportedSignatureAlgorithms)
+ {
+ if (supportedSignatureAlgorithms != null)
+ {
+ foreach (SignatureAndHashAlgorithm signatureAndHashAlgorithm in supportedSignatureAlgorithms)
+ {
+ /*
+ * TODO We could validate the signature algorithm part. Currently the impact is
+ * that we might be tracking extra hashes pointlessly (but there are only a
+ * limited number of recognized hash algorithms).
+ */
+ int signatureScheme = SignatureScheme.From(signatureAndHashAlgorithm);
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ if (cryptoHashAlgorithm >= 0)
+ {
+ handshakeHash.TrackHashAlgorithm(cryptoHashAlgorithm);
+ }
+ else if (HashAlgorithm.Intrinsic == signatureAndHashAlgorithm.Hash)
+ {
+ handshakeHash.ForceBuffering();
+ }
+ }
+ }
+ }
+
+ public static bool HasSigningCapability(short clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_sign:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static IList VectorOfOne(object obj)
+ {
+ IList v = Platform.CreateArrayList(1);
+ v.Add(obj);
+ return v;
+ }
+
+ public static int GetCipherType(int cipherSuite)
+ {
+ int encryptionAlgorithm = GetEncryptionAlgorithm(cipherSuite);
+
+ return GetEncryptionAlgorithmType(encryptionAlgorithm);
+ }
+
+ public static int GetEncryptionAlgorithm(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_DH_anon_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_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_anon_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_DH_anon_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_anon_WITH_AES_128_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_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ 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_AES_128_CCM_SHA256:
+ 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_ECDHE_PSK_WITH_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return EncryptionAlgorithm.AES_128_CCM;
+
+ case CipherSuite.TLS_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256:
+ 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_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+ 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_PSK_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_anon_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_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_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_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_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ 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_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_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_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_PSK_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_anon_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256:
+ return EncryptionAlgorithm.ARIA_128_CBC;
+
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256:
+ return EncryptionAlgorithm.ARIA_128_GCM;
+
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384:
+ return EncryptionAlgorithm.ARIA_256_CBC;
+
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384:
+ return EncryptionAlgorithm.ARIA_256_GCM;
+
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ 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_SHA:
+ 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_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ return EncryptionAlgorithm.CAMELLIA_128_CBC;
+
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+ 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_anon_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_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_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_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_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ 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_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ return EncryptionAlgorithm.CAMELLIA_256_CBC;
+
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ return EncryptionAlgorithm.CHACHA20_POLY1305;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_anon_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_SEED_CBC_SHA:
+ 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;
+
+ case CipherSuite.TLS_SM4_CCM_SM3:
+ return EncryptionAlgorithm.SM4_CCM;
+
+ case CipherSuite.TLS_SM4_GCM_SM3:
+ return EncryptionAlgorithm.SM4_GCM;
+
+ default:
+ return -1;
+ }
+ }
+
+ public static int GetEncryptionAlgorithmType(int encryptionAlgorithm)
+ {
+ switch (encryptionAlgorithm)
+ {
+ case EncryptionAlgorithm.AES_128_CCM:
+ case EncryptionAlgorithm.AES_128_CCM_8:
+ case EncryptionAlgorithm.AES_128_GCM:
+ case EncryptionAlgorithm.AES_256_CCM:
+ case EncryptionAlgorithm.AES_256_CCM_8:
+ case EncryptionAlgorithm.AES_256_GCM:
+ case EncryptionAlgorithm.ARIA_128_GCM:
+ case EncryptionAlgorithm.ARIA_256_GCM:
+ case EncryptionAlgorithm.CAMELLIA_128_GCM:
+ case EncryptionAlgorithm.CAMELLIA_256_GCM:
+ case EncryptionAlgorithm.CHACHA20_POLY1305:
+ case EncryptionAlgorithm.SM4_CCM:
+ case EncryptionAlgorithm.SM4_GCM:
+ 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.ARIA_128_CBC:
+ case EncryptionAlgorithm.ARIA_256_CBC:
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ case EncryptionAlgorithm.SEED_CBC:
+ case EncryptionAlgorithm.SM4_CBC:
+ return CipherType.block;
+
+ case EncryptionAlgorithm.NULL:
+ case EncryptionAlgorithm.RC4_40:
+ case EncryptionAlgorithm.RC4_128:
+ return CipherType.stream;
+
+ default:
+ return -1;
+ }
+ }
+
+ public static int GetKeyExchangeAlgorithm(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA:
+ 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_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_SEED_CBC_SHA:
+ return KeyExchangeAlgorithm.DH_anon;
+
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_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_CHACHA20_POLY1305_SHA256:
+ 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_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ return KeyExchangeAlgorithm.DHE_RSA;
+
+ 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:
+ case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA:
+ return KeyExchangeAlgorithm.ECDH_anon;
+
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_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:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_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:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_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_NULL_SHA:
+ 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_128_CCM_8_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_GCM_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_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_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_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_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_NULL_SHA:
+ return KeyExchangeAlgorithm.ECDHE_RSA;
+
+ case CipherSuite.TLS_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_SM4_CCM_SM3:
+ case CipherSuite.TLS_SM4_GCM_SM3:
+ return KeyExchangeAlgorithm.NULL;
+
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ARIA_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_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_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_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ 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:
+ return -1;
+ }
+ }
+
+ public static IList GetKeyExchangeAlgorithms(int[] cipherSuites)
+ {
+ IList result = Platform.CreateArrayList();
+ if (null != cipherSuites)
+ {
+ for (int i = 0; i < cipherSuites.Length; ++i)
+ {
+ AddToSet(result, GetKeyExchangeAlgorithm(cipherSuites[i]));
+ }
+ result.Remove(-1);
+ }
+ return result;
+ }
+
+ public static int GetMacAlgorithm(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_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_CHACHA20_POLY1305_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_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_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_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ 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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_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_PSK_WITH_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ 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_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_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_PSK_WITH_CHACHA20_POLY1305_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_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_SM4_CCM_SM3:
+ case CipherSuite.TLS_SM4_GCM_SM3:
+ return MacAlgorithm.cls_null;
+
+ case CipherSuite.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_anon_WITH_SEED_CBC_SHA:
+ 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_NULL_SHA:
+ 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_SEED_CBC_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:
+ case CipherSuite.TLS_ECDH_anon_WITH_NULL_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_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_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_NULL_SHA:
+ 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_NULL_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_ECDHE_RSA_WITH_NULL_SHA:
+ 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_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ 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_NULL_SHA:
+ 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_anon_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_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_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_128_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_ARIA_128_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_ARIA_128_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_ARIA_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_ARIA_128_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_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_128_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_DH_anon_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_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_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_CBC_SHA384:
+ return MacAlgorithm.hmac_sha384;
+
+ default:
+ return -1;
+ }
+ }
+
+ public static ProtocolVersion GetMinimumVersion(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_SM4_CCM_SM3:
+ case CipherSuite.TLS_SM4_GCM_SM3:
+ return ProtocolVersion.TLSv13;
+
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_anon_WITH_ARIA_256_GCM_SHA384:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ARIA_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_CHACHA20_POLY1305_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_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_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_PSK_WITH_AES_128_CCM_8_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ARIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ 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_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ARIA_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_PSK_WITH_CHACHA20_POLY1305_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_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_ARIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ARIA_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 IList GetNamedGroupRoles(int[] cipherSuites)
+ {
+ return GetNamedGroupRoles(GetKeyExchangeAlgorithms(cipherSuites));
+ }
+
+ public static IList GetNamedGroupRoles(IList keyExchangeAlgorithms)
+ {
+ IList result = Platform.CreateArrayList();
+ foreach (int keyExchangeAlgorithm in keyExchangeAlgorithms)
+ {
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DH_anon:
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ {
+ AddToSet(result, NamedGroupRole.dh);
+ break;
+ }
+
+ case KeyExchangeAlgorithm.ECDH_anon:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ {
+ AddToSet(result, NamedGroupRole.ecdh);
+ break;
+ }
+
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ {
+ AddToSet(result, NamedGroupRole.ecdh);
+ AddToSet(result, NamedGroupRole.ecdsa);
+ break;
+ }
+
+ case KeyExchangeAlgorithm.NULL:
+ {
+ // TODO[tls13] We're conservatively adding both here, though maybe only one is needed
+ AddToSet(result, NamedGroupRole.dh);
+ AddToSet(result, NamedGroupRole.ecdh);
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool IsAeadCipherSuite(int cipherSuite)
+ {
+ return CipherType.aead == GetCipherType(cipherSuite);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool IsBlockCipherSuite(int cipherSuite)
+ {
+ return CipherType.block == GetCipherType(cipherSuite);
+ }
+
+ /// <exception cref="IOException"/>
+ public static bool IsStreamCipherSuite(int cipherSuite)
+ {
+ return CipherType.stream == GetCipherType(cipherSuite);
+ }
+
+ /// <returns>Whether a server can select the specified cipher suite given the available signature algorithms
+ /// for ServerKeyExchange.</returns>
+ public static bool IsValidCipherSuiteForSignatureAlgorithms(int cipherSuite, IList sigAlgs)
+ {
+ int keyExchangeAlgorithm = GetKeyExchangeAlgorithm(cipherSuite);
+
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.NULL:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ break;
+
+ default:
+ return true;
+ }
+
+ foreach (short signatureAlgorithm in sigAlgs)
+ {
+ if (IsValidSignatureAlgorithmForServerKeyExchange(signatureAlgorithm, keyExchangeAlgorithm))
+ return true;
+ }
+
+ return false;
+ }
+
+ internal static bool IsValidCipherSuiteSelection(int[] offeredCipherSuites, int cipherSuite)
+ {
+ return null != offeredCipherSuites
+ && Arrays.Contains(offeredCipherSuites, cipherSuite)
+ && CipherSuite.TLS_NULL_WITH_NULL_NULL != cipherSuite
+ && !CipherSuite.IsScsv(cipherSuite);
+ }
+
+ internal static bool IsValidKeyShareSelection(ProtocolVersion negotiatedVersion, int[] clientSupportedGroups,
+ IDictionary clientAgreements, int keyShareGroup)
+ {
+ return null != clientSupportedGroups
+ && Arrays.Contains(clientSupportedGroups, keyShareGroup)
+ && !clientAgreements.Contains(keyShareGroup)
+ && NamedGroup.CanBeNegotiated(keyShareGroup, negotiatedVersion);
+ }
+
+ internal static bool IsValidSignatureAlgorithmForCertificateVerify(short signatureAlgorithm,
+ short[] clientCertificateTypes)
+ {
+ short clientCertificateType = SignatureAlgorithm.GetClientCertificateType(signatureAlgorithm);
+
+ return clientCertificateType >= 0 && Arrays.Contains(clientCertificateTypes, clientCertificateType);
+ }
+
+ internal static bool IsValidSignatureAlgorithmForServerKeyExchange(short signatureAlgorithm,
+ int keyExchangeAlgorithm)
+ {
+ // TODO[tls13]
+
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return true;
+ default:
+ return false;
+ }
+
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ return SignatureAlgorithm.dsa == signatureAlgorithm;
+
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.ecdsa:
+ case SignatureAlgorithm.ed25519:
+ case SignatureAlgorithm.ed448:
+ return true;
+ default:
+ return false;
+ }
+
+ case KeyExchangeAlgorithm.NULL:
+ return SignatureAlgorithm.anonymous != signatureAlgorithm;
+
+ default:
+ return false;
+ }
+ }
+
+ public static bool IsValidSignatureSchemeForServerKeyExchange(int signatureScheme, int keyExchangeAlgorithm)
+ {
+ short signatureAlgorithm = SignatureScheme.GetSignatureAlgorithm(signatureScheme);
+
+ return IsValidSignatureAlgorithmForServerKeyExchange(signatureAlgorithm, keyExchangeAlgorithm);
+ }
+
+ public static bool IsValidVersionForCipherSuite(int cipherSuite, ProtocolVersion version)
+ {
+ version = version.GetEquivalentTlsVersion();
+
+ ProtocolVersion minimumVersion = GetMinimumVersion(cipherSuite);
+ if (minimumVersion == version)
+ return true;
+
+ if (!minimumVersion.IsEarlierVersionOf(version))
+ return false;
+
+ return ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(minimumVersion)
+ || ProtocolVersion.TLSv13.IsLaterVersionOf(version);
+ }
+
+ /// <exception cref="IOException"/>
+ public static SignatureAndHashAlgorithm ChooseSignatureAndHashAlgorithm(TlsContext context, IList sigHashAlgs,
+ short signatureAlgorithm)
+ {
+ return ChooseSignatureAndHashAlgorithm(context.ServerVersion, sigHashAlgs, signatureAlgorithm);
+ }
+
+ /// <exception cref="IOException"/>
+ public static SignatureAndHashAlgorithm ChooseSignatureAndHashAlgorithm(ProtocolVersion negotiatedVersion,
+ IList sigHashAlgs, short signatureAlgorithm)
+ {
+ if (!IsTlsV12(negotiatedVersion))
+ return null;
+
+
+ if (sigHashAlgs == null)
+ {
+ /*
+ * TODO[tls13] RFC 8446 4.2.3 Clients which desire the server to authenticate itself via
+ * a certificate MUST send the "signature_algorithms" extension.
+ */
+
+ sigHashAlgs = GetDefaultSignatureAlgorithms(signatureAlgorithm);
+ }
+
+ SignatureAndHashAlgorithm result = null;
+ foreach (SignatureAndHashAlgorithm sigHashAlg in sigHashAlgs)
+ {
+ if (sigHashAlg.Signature != signatureAlgorithm)
+ continue;
+
+ short hash = sigHashAlg.Hash;
+ if (hash < MinimumHashStrict)
+ continue;
+
+ if (result == null)
+ {
+ result = sigHashAlg;
+ continue;
+ }
+
+ short current = result.Hash;
+ if (current < MinimumHashPreferred)
+ {
+ if (hash > current)
+ {
+ result = sigHashAlg;
+ }
+ }
+ else if (hash >= MinimumHashPreferred)
+ {
+ if (hash < current)
+ {
+ result = sigHashAlg;
+ }
+ }
+ }
+ if (result == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return result;
+ }
+
+ public static IList GetUsableSignatureAlgorithms(IList sigHashAlgs)
+ {
+ if (sigHashAlgs == null)
+ {
+ IList v = Platform.CreateArrayList(3);
+ v.Add(SignatureAlgorithm.rsa);
+ v.Add(SignatureAlgorithm.dsa);
+ v.Add(SignatureAlgorithm.ecdsa);
+ return v;
+ }
+ else
+ {
+ IList v = Platform.CreateArrayList();
+ foreach (SignatureAndHashAlgorithm sigHashAlg in sigHashAlgs)
+ {
+ if (sigHashAlg.Hash >= MinimumHashStrict)
+ {
+ short sigAlg = sigHashAlg.Signature;
+ if (!v.Contains(sigAlg))
+ {
+ // TODO Check for crypto support before choosing (or pass in cached list?)
+ v.Add(sigAlg);
+ }
+ }
+ }
+ return v;
+ }
+ }
+
+ public static int GetCommonCipherSuite13(ProtocolVersion negotiatedVersion, int[] peerCipherSuites,
+ int[] localCipherSuites, bool useLocalOrder)
+ {
+ int[] ordered = peerCipherSuites, unordered = localCipherSuites;
+ if (useLocalOrder)
+ {
+ ordered = localCipherSuites;
+ unordered = peerCipherSuites;
+ }
+
+ for (int i = 0; i < ordered.Length; ++i)
+ {
+ int candidate = ordered[i];
+ if (Arrays.Contains(unordered, candidate) &&
+ IsValidVersionForCipherSuite(candidate, negotiatedVersion))
+ {
+ return candidate;
+ }
+ }
+
+ return -1;
+ }
+
+ public static int[] GetCommonCipherSuites(int[] peerCipherSuites, int[] localCipherSuites, bool useLocalOrder)
+ {
+ int[] ordered = peerCipherSuites, unordered = localCipherSuites;
+ if (useLocalOrder)
+ {
+ ordered = localCipherSuites;
+ unordered = peerCipherSuites;
+ }
+
+ int count = 0, limit = System.Math.Min(ordered.Length, unordered.Length);
+ int[] candidates = new int[limit];
+ for (int i = 0; i < ordered.Length; ++i)
+ {
+ int candidate = ordered[i];
+ if (!Contains(candidates, 0, count, candidate)
+ && Arrays.Contains(unordered, candidate))
+ {
+ candidates[count++] = candidate;
+ }
+ }
+
+ if (count < limit)
+ {
+ candidates = Arrays.CopyOf(candidates, count);
+ }
+
+ return candidates;
+ }
+
+ public static int[] GetSupportedCipherSuites(TlsCrypto crypto, int[] suites)
+ {
+ return GetSupportedCipherSuites(crypto, suites, 0, suites.Length);
+ }
+
+ public static int[] GetSupportedCipherSuites(TlsCrypto crypto, int[] suites, int suitesOff, int suitesCount)
+ {
+ int[] supported = new int[suitesCount];
+ int count = 0;
+
+ for (int i = 0; i < suitesCount; ++i)
+ {
+ int suite = suites[suitesOff + i];
+ if (IsSupportedCipherSuite(crypto, suite))
+ {
+ supported[count++] = suite;
+ }
+ }
+
+ if (count < suitesCount)
+ {
+ supported = Arrays.CopyOf(supported, count);
+ }
+
+ return supported;
+ }
+
+ public static bool IsSupportedCipherSuite(TlsCrypto crypto, int cipherSuite)
+ {
+ return IsSupportedKeyExchange(crypto, GetKeyExchangeAlgorithm(cipherSuite))
+ && crypto.HasEncryptionAlgorithm(GetEncryptionAlgorithm(cipherSuite))
+ && crypto.HasMacAlgorithm(GetMacAlgorithm(cipherSuite));
+ }
+
+ public static bool IsSupportedKeyExchange(TlsCrypto crypto, int keyExchangeAlgorithm)
+ {
+ switch (keyExchangeAlgorithm)
+ {
+ case KeyExchangeAlgorithm.DH_anon:
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DHE_PSK:
+ return crypto.HasDHAgreement();
+
+ case KeyExchangeAlgorithm.DHE_DSS:
+ return crypto.HasDHAgreement()
+ && crypto.HasSignatureAlgorithm(SignatureAlgorithm.dsa);
+
+ case KeyExchangeAlgorithm.DHE_RSA:
+ return crypto.HasDHAgreement()
+ && HasAnyRsaSigAlgs(crypto);
+
+ case KeyExchangeAlgorithm.ECDH_anon:
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ return crypto.HasECDHAgreement();
+
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ return crypto.HasECDHAgreement()
+ && (crypto.HasSignatureAlgorithm(SignatureAlgorithm.ecdsa)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.ed25519)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.ed448));
+
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ return crypto.HasECDHAgreement()
+ && HasAnyRsaSigAlgs(crypto);
+
+ case KeyExchangeAlgorithm.NULL:
+ case KeyExchangeAlgorithm.PSK:
+ return true;
+
+ case KeyExchangeAlgorithm.RSA:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ return crypto.HasRsaEncryption();
+
+ case KeyExchangeAlgorithm.SRP:
+ return crypto.HasSrpAuthentication();
+
+ case KeyExchangeAlgorithm.SRP_DSS:
+ return crypto.HasSrpAuthentication()
+ && crypto.HasSignatureAlgorithm(SignatureAlgorithm.dsa);
+
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return crypto.HasSrpAuthentication()
+ && HasAnyRsaSigAlgs(crypto);
+
+ default:
+ return false;
+ }
+ }
+
+ internal static bool HasAnyRsaSigAlgs(TlsCrypto crypto)
+ {
+ return crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa_pss_rsae_sha256)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa_pss_rsae_sha384)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa_pss_rsae_sha512)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa_pss_pss_sha256)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa_pss_pss_sha384)
+ || crypto.HasSignatureAlgorithm(SignatureAlgorithm.rsa_pss_pss_sha512);
+ }
+
+ internal static byte[] GetCurrentPrfHash(TlsHandshakeHash handshakeHash)
+ {
+ return handshakeHash.ForkPrfHash().CalculateHash();
+ }
+
+ internal static void SealHandshakeHash(TlsContext context, TlsHandshakeHash handshakeHash, bool forceBuffering)
+ {
+ if (forceBuffering || !context.Crypto.HasAllRawSignatureAlgorithms())
+ {
+ handshakeHash.ForceBuffering();
+ }
+
+ handshakeHash.SealHashAlgorithms();
+ }
+
+ private static TlsHash CreateHash(TlsCrypto crypto, short hashAlgorithm)
+ {
+ int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm);
+
+ return crypto.CreateHash(cryptoHashAlgorithm);
+ }
+
+ /// <exception cref="IOException"/>
+ private static TlsKeyExchange CreateKeyExchangeClient(TlsClient client, int keyExchange)
+ {
+ TlsKeyExchangeFactory factory = client.GetKeyExchangeFactory();
+
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_anon:
+ return factory.CreateDHanonKeyExchangeClient(keyExchange, client.GetDHGroupVerifier());
+
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DH_RSA:
+ return factory.CreateDHKeyExchange(keyExchange);
+
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ return factory.CreateDheKeyExchangeClient(keyExchange, client.GetDHGroupVerifier());
+
+ case KeyExchangeAlgorithm.ECDH_anon:
+ return factory.CreateECDHanonKeyExchangeClient(keyExchange);
+
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ return factory.CreateECDHKeyExchange(keyExchange);
+
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ return factory.CreateECDheKeyExchangeClient(keyExchange);
+
+ case KeyExchangeAlgorithm.RSA:
+ return factory.CreateRsaKeyExchange(keyExchange);
+
+ case KeyExchangeAlgorithm.DHE_PSK:
+ return factory.CreatePskKeyExchangeClient(keyExchange, client.GetPskIdentity(),
+ client.GetDHGroupVerifier());
+
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ return factory.CreatePskKeyExchangeClient(keyExchange, client.GetPskIdentity(), null);
+
+ case KeyExchangeAlgorithm.SRP:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return factory.CreateSrpKeyExchangeClient(keyExchange, client.GetSrpIdentity(),
+ client.GetSrpConfigVerifier());
+
+ 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);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ private static TlsKeyExchange CreateKeyExchangeServer(TlsServer server, int keyExchange)
+ {
+ TlsKeyExchangeFactory factory = server.GetKeyExchangeFactory();
+
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_anon:
+ return factory.CreateDHanonKeyExchangeServer(keyExchange, server.GetDHConfig());
+
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DH_RSA:
+ return factory.CreateDHKeyExchange(keyExchange);
+
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ return factory.CreateDheKeyExchangeServer(keyExchange, server.GetDHConfig());
+
+ case KeyExchangeAlgorithm.ECDH_anon:
+ return factory.CreateECDHanonKeyExchangeServer(keyExchange, server.GetECDHConfig());
+
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ return factory.CreateECDHKeyExchange(keyExchange);
+
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ return factory.CreateECDheKeyExchangeServer(keyExchange, server.GetECDHConfig());
+
+ case KeyExchangeAlgorithm.RSA:
+ return factory.CreateRsaKeyExchange(keyExchange);
+
+ case KeyExchangeAlgorithm.DHE_PSK:
+ return factory.CreatePskKeyExchangeServer(keyExchange, server.GetPskIdentityManager(),
+ server.GetDHConfig(), null);
+
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ return factory.CreatePskKeyExchangeServer(keyExchange, server.GetPskIdentityManager(), null,
+ server.GetECDHConfig());
+
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ return factory.CreatePskKeyExchangeServer(keyExchange, server.GetPskIdentityManager(), null, null);
+
+ case KeyExchangeAlgorithm.SRP:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return factory.CreateSrpKeyExchangeServer(keyExchange, server.GetSrpLoginParameters());
+
+ 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);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal static TlsKeyExchange InitKeyExchangeClient(TlsClientContext clientContext, TlsClient client)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ TlsKeyExchange keyExchange = CreateKeyExchangeClient(client, securityParameters.KeyExchangeAlgorithm);
+ keyExchange.Init(clientContext);
+ return keyExchange;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static TlsKeyExchange InitKeyExchangeServer(TlsServerContext serverContext, TlsServer server)
+ {
+ SecurityParameters securityParameters = serverContext.SecurityParameters;
+ TlsKeyExchange keyExchange = CreateKeyExchangeServer(server, securityParameters.KeyExchangeAlgorithm);
+ keyExchange.Init(serverContext);
+ return keyExchange;
+ }
+
+ internal static TlsCipher InitCipher(TlsContext context)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ int cipherSuite = securityParameters.CipherSuite;
+ int encryptionAlgorithm = GetEncryptionAlgorithm(cipherSuite);
+ int macAlgorithm = GetMacAlgorithm(cipherSuite);
+
+ if (encryptionAlgorithm < 0 || macAlgorithm < 0)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return context.Crypto.CreateCipher(new TlsCryptoParameters(context), encryptionAlgorithm, macAlgorithm);
+ }
+
+ /// <summary>Check the signature algorithm for certificates in the peer's CertPath as specified in RFC 5246
+ /// 7.4.2, 7.4.4, 7.4.6 and similar rules for earlier TLS versions.</summary>
+ /// <remarks>
+ /// The supplied CertPath should include the trust anchor (its signature algorithm isn't checked, but in the
+ /// general case checking a certificate requires the issuer certificate).
+ /// </remarks>
+ /// <exception cref="IOException">if any certificate in the CertPath (excepting the trust anchor) has a
+ /// signature algorithm that is not one of the locally supported signature algorithms.</exception>
+ public static void CheckPeerSigAlgs(TlsContext context, TlsCertificate[] peerCertPath)
+ {
+ if (context.IsServer)
+ {
+ CheckSigAlgOfClientCerts(context, peerCertPath);
+ }
+ else
+ {
+ CheckSigAlgOfServerCerts(context, peerCertPath);
+ }
+ }
+
+ private static void CheckSigAlgOfClientCerts(TlsContext context, TlsCertificate[] clientCertPath)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ short[] clientCertTypes = securityParameters.ClientCertTypes;
+ IList serverSigAlgsCert = securityParameters.ServerSigAlgsCert;
+
+ int trustAnchorPos = clientCertPath.Length - 1;
+ for (int i = 0; i < trustAnchorPos; ++i)
+ {
+ TlsCertificate subjectCert = clientCertPath[i];
+ TlsCertificate issuerCert = clientCertPath[i + 1];
+
+ SignatureAndHashAlgorithm sigAndHashAlg = GetCertSigAndHashAlg(subjectCert, issuerCert);
+
+ bool valid = false;
+ if (null == sigAndHashAlg)
+ {
+ // We don't recognize the 'signatureAlgorithm' of the certificate
+ }
+ else if (null == serverSigAlgsCert)
+ {
+ // TODO Review this (legacy) logic with RFC 4346 (7.4?.2?)
+ if (null != clientCertTypes)
+ {
+ for (int j = 0; j < clientCertTypes.Length; ++j)
+ {
+ short signatureAlgorithm = GetLegacySignatureAlgorithmClientCert(clientCertTypes[j]);
+ if (sigAndHashAlg.Signature == signatureAlgorithm)
+ {
+ valid = true;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * RFC 5246 7.4.4 Any certificates provided by the client MUST be signed using a
+ * hash/signature algorithm pair found in supported_signature_algorithms.
+ */
+ valid = ContainsSignatureAlgorithm(serverSigAlgsCert, sigAndHashAlg);
+ }
+
+ if (!valid)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+ }
+ }
+
+ private static void CheckSigAlgOfServerCerts(TlsContext context, TlsCertificate[] serverCertPath)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ IList clientSigAlgsCert = securityParameters.ClientSigAlgsCert;
+ IList clientSigAlgs = securityParameters.ClientSigAlgs;
+
+ /*
+ * NOTE: For TLS 1.2, we'll check 'signature_algorithms' too (if it's distinct), since
+ * there's no way of knowing whether the server understood 'signature_algorithms_cert'.
+ */
+ if (clientSigAlgs == clientSigAlgsCert || IsTlsV13(securityParameters.NegotiatedVersion))
+ {
+ clientSigAlgs = null;
+ }
+
+ int trustAnchorPos = serverCertPath.Length - 1;
+ for (int i = 0; i < trustAnchorPos; ++i)
+ {
+ TlsCertificate subjectCert = serverCertPath[i];
+ TlsCertificate issuerCert = serverCertPath[i + 1];
+
+ SignatureAndHashAlgorithm sigAndHashAlg = GetCertSigAndHashAlg(subjectCert, issuerCert);
+
+ bool valid = false;
+ if (null == sigAndHashAlg)
+ {
+ // We don't recognize the 'signatureAlgorithm' of the certificate
+ }
+ else if (null == clientSigAlgsCert)
+ {
+ /*
+ * RFC 4346 7.4.2. Unless otherwise specified, the signing algorithm for the
+ * certificate MUST be the same as the algorithm for the certificate key.
+ */
+ short signatureAlgorithm = GetLegacySignatureAlgorithmServerCert(
+ securityParameters.KeyExchangeAlgorithm);
+
+ valid = (signatureAlgorithm == sigAndHashAlg.Signature);
+ }
+ else
+ {
+ /*
+ * RFC 5246 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.
+ */
+ valid = ContainsSignatureAlgorithm(clientSigAlgsCert, sigAndHashAlg)
+ || (null != clientSigAlgs && ContainsSignatureAlgorithm(clientSigAlgs, sigAndHashAlg));
+ }
+
+ if (!valid)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+ }
+
+ internal static void CheckTlsFeatures(Certificate serverCertificate, IDictionary clientExtensions,
+ IDictionary serverExtensions)
+ {
+ /*
+ * RFC 7633 4.3.3. A client MUST treat a certificate with a TLS feature extension as an
+ * invalid certificate if the features offered by the server do not contain all features
+ * present in both the client's ClientHello message and the TLS feature extension.
+ */
+ byte[] tlsFeatures = serverCertificate.GetCertificateAt(0).GetExtension(TlsObjectIdentifiers.id_pe_tlsfeature);
+ if (tlsFeatures != null)
+ {
+ foreach (DerInteger tlsExtension in Asn1Sequence.GetInstance(ReadDerObject(tlsFeatures)))
+ {
+ int extensionType = tlsExtension.IntValueExact;
+ CheckUint16(extensionType);
+
+ if (clientExtensions.Contains(extensionType) && !serverExtensions.Contains(extensionType))
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+ }
+ }
+
+ internal static void ProcessClientCertificate(TlsServerContext serverContext, Certificate clientCertificate,
+ TlsKeyExchange keyExchange, TlsServer server)
+ {
+ SecurityParameters securityParameters = serverContext.SecurityParameters;
+ if (null != securityParameters.PeerCertificate)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ bool isTlsV13 = IsTlsV13(securityParameters.NegotiatedVersion);
+ if (isTlsV13)
+ {
+ // 'keyExchange' not used
+ }
+ else if (clientCertificate.IsEmpty)
+ {
+ /*
+ * NOTE: We tolerate SSLv3 clients sending an empty chain, although "If no suitable
+ * certificate is available, the client should send a no_certificate alert instead".
+ */
+
+ keyExchange.SkipClientCredentials();
+ }
+ else
+ {
+ keyExchange.ProcessClientCertificate(clientCertificate);
+ }
+
+ securityParameters.m_peerCertificate = 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.
+ */
+ server.NotifyClientCertificate(clientCertificate);
+ }
+
+ internal static void ProcessServerCertificate(TlsClientContext clientContext,
+ CertificateStatus serverCertificateStatus, TlsKeyExchange keyExchange,
+ TlsAuthentication clientAuthentication, IDictionary clientExtensions, IDictionary serverExtensions)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ bool isTlsV13 = IsTlsV13(securityParameters.NegotiatedVersion);
+
+ if (null == clientAuthentication)
+ {
+ if (isTlsV13)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ // There was no server certificate message; check it's OK
+ keyExchange.SkipServerCredentials();
+ securityParameters.m_tlsServerEndPoint = EmptyBytes;
+ return;
+ }
+
+ Certificate serverCertificate = securityParameters.PeerCertificate;
+
+ CheckTlsFeatures(serverCertificate, clientExtensions, serverExtensions);
+
+ if (!isTlsV13)
+ {
+ keyExchange.ProcessServerCertificate(serverCertificate);
+ }
+
+ clientAuthentication.NotifyServerCertificate(
+ new TlsServerCertificateImpl(serverCertificate, serverCertificateStatus));
+ }
+
+ internal static SignatureAndHashAlgorithm GetCertSigAndHashAlg(TlsCertificate subjectCert, TlsCertificate issuerCert)
+ {
+ string sigAlgOid = subjectCert.SigAlgOid;
+
+ if (null != sigAlgOid)
+ {
+ if (!PkcsObjectIdentifiers.IdRsassaPss.Id.Equals(sigAlgOid))
+ {
+ if (!CertSigAlgOids.Contains(sigAlgOid))
+ return null;
+
+ return (SignatureAndHashAlgorithm)CertSigAlgOids[sigAlgOid];
+ }
+
+ RsassaPssParameters pssParams = RsassaPssParameters.GetInstance(subjectCert.GetSigAlgParams());
+ if (null != pssParams)
+ {
+ DerObjectIdentifier hashOid = pssParams.HashAlgorithm.Algorithm;
+ if (NistObjectIdentifiers.IdSha256.Equals(hashOid))
+ {
+ if (issuerCert.SupportsSignatureAlgorithmCA(SignatureAlgorithm.rsa_pss_pss_sha256))
+ return SignatureAndHashAlgorithm.rsa_pss_pss_sha256;
+
+ if (issuerCert.SupportsSignatureAlgorithmCA(SignatureAlgorithm.rsa_pss_rsae_sha256))
+ return SignatureAndHashAlgorithm.rsa_pss_rsae_sha256;
+ }
+ else if (NistObjectIdentifiers.IdSha384.Equals(hashOid))
+ {
+ if (issuerCert.SupportsSignatureAlgorithmCA(SignatureAlgorithm.rsa_pss_pss_sha384))
+ return SignatureAndHashAlgorithm.rsa_pss_pss_sha384;
+
+ if (issuerCert.SupportsSignatureAlgorithmCA(SignatureAlgorithm.rsa_pss_rsae_sha384))
+ return SignatureAndHashAlgorithm.rsa_pss_rsae_sha384;
+ }
+ else if (NistObjectIdentifiers.IdSha512.Equals(hashOid))
+ {
+ if (issuerCert.SupportsSignatureAlgorithmCA(SignatureAlgorithm.rsa_pss_pss_sha512))
+ return SignatureAndHashAlgorithm.rsa_pss_pss_sha512;
+
+ if (issuerCert.SupportsSignatureAlgorithmCA(SignatureAlgorithm.rsa_pss_rsae_sha512))
+ return SignatureAndHashAlgorithm.rsa_pss_rsae_sha512;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ internal static CertificateRequest ValidateCertificateRequest(CertificateRequest certificateRequest,
+ TlsKeyExchange keyExchange)
+ {
+ short[] validClientCertificateTypes = keyExchange.GetClientCertificateTypes();
+ if (IsNullOrEmpty(validClientCertificateTypes))
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ certificateRequest = NormalizeCertificateRequest(certificateRequest, validClientCertificateTypes);
+ if (certificateRequest == null)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return certificateRequest;
+ }
+
+ internal static CertificateRequest NormalizeCertificateRequest(CertificateRequest certificateRequest,
+ short[] validClientCertificateTypes)
+ {
+ if (ContainsAll(validClientCertificateTypes, certificateRequest.CertificateTypes))
+ return certificateRequest;
+
+ short[] retained = RetainAll(certificateRequest.CertificateTypes, validClientCertificateTypes);
+ if (retained.Length < 1)
+ return null;
+
+ // TODO Filter for unique sigAlgs/CAs only
+ return new CertificateRequest(retained, certificateRequest.SupportedSignatureAlgorithms,
+ certificateRequest.CertificateAuthorities);
+ }
+
+ internal static bool Contains(int[] buf, int off, int len, int value)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ if (value == buf[off + i])
+ return true;
+ }
+ return false;
+ }
+
+ internal static bool ContainsAll(short[] container, short[] elements)
+ {
+ for (int i = 0; i < elements.Length; ++i)
+ {
+ if (!Arrays.Contains(container, elements[i]))
+ return false;
+ }
+ return true;
+ }
+
+ internal static short[] RetainAll(short[] retainer, short[] elements)
+ {
+ short[] retained = new short[System.Math.Min(retainer.Length, elements.Length)];
+
+ int count = 0;
+ for (int i = 0; i < elements.Length; ++i)
+ {
+ if (Arrays.Contains(retainer, elements[i]))
+ {
+ retained[count++] = elements[i];
+ }
+ }
+
+ return Truncate(retained, count);
+ }
+
+ internal static short[] Truncate(short[] a, int n)
+ {
+ if (n < a.Length)
+ return a;
+
+ short[] t = new short[n];
+ Array.Copy(a, 0, t, 0, n);
+ return t;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static TlsCredentialedAgreement RequireAgreementCredentials(TlsCredentials credentials)
+ {
+ if (!(credentials is TlsCredentialedAgreement))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return (TlsCredentialedAgreement)credentials;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static TlsCredentialedDecryptor RequireDecryptorCredentials(TlsCredentials credentials)
+ {
+ if (!(credentials is TlsCredentialedDecryptor))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return (TlsCredentialedDecryptor)credentials;
+ }
+
+ /// <exception cref="IOException"/>
+ internal static TlsCredentialedSigner RequireSignerCredentials(TlsCredentials credentials)
+ {
+ if (!(credentials is TlsCredentialedSigner))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return (TlsCredentialedSigner)credentials;
+ }
+
+ private static void CheckDowngradeMarker(byte[] randomBlock, byte[] downgradeMarker)
+ {
+ int len = downgradeMarker.Length;
+ if (ConstantTimeAreEqual(len, downgradeMarker, 0, randomBlock, randomBlock.Length - len))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ internal static void CheckDowngradeMarker(ProtocolVersion version, byte[] randomBlock)
+ {
+ version = version.GetEquivalentTlsVersion();
+
+ if (version.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv11))
+ {
+ CheckDowngradeMarker(randomBlock, DowngradeTlsV11);
+ }
+ if (version.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv12))
+ {
+ CheckDowngradeMarker(randomBlock, DowngradeTlsV12);
+ }
+ }
+
+ internal static void WriteDowngradeMarker(ProtocolVersion version, byte[] randomBlock)
+ {
+ version = version.GetEquivalentTlsVersion();
+
+ byte[] marker;
+ if (ProtocolVersion.TLSv12 == version)
+ {
+ marker = DowngradeTlsV12;
+ }
+ else if (version.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv11))
+ {
+ marker = DowngradeTlsV11;
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ Array.Copy(marker, 0, randomBlock, randomBlock.Length - marker.Length, marker.Length);
+ }
+
+ internal static TlsAuthentication ReceiveServerCertificate(TlsClientContext clientContext, TlsClient client,
+ MemoryStream buf)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ if (null != securityParameters.PeerCertificate)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ MemoryStream endPointHash = new MemoryStream();
+
+ Certificate.ParseOptions options = new Certificate.ParseOptions()
+ .SetMaxChainLength(client.GetMaxCertificateChainLength());
+
+ Certificate serverCertificate = Certificate.Parse(options, clientContext, buf, endPointHash);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ securityParameters.m_peerCertificate = serverCertificate;
+ securityParameters.m_tlsServerEndPoint = endPointHash.ToArray();
+
+ TlsAuthentication authentication = client.GetAuthentication();
+ if (null == authentication)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return authentication;
+ }
+
+ internal static TlsAuthentication Receive13ServerCertificate(TlsClientContext clientContext, TlsClient client,
+ MemoryStream buf)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ if (null != securityParameters.PeerCertificate)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ Certificate.ParseOptions options = new Certificate.ParseOptions()
+ .SetMaxChainLength(client.GetMaxCertificateChainLength());
+
+ Certificate serverCertificate = Certificate.Parse(options, clientContext, buf, null);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ if (serverCertificate.GetCertificateRequestContext().Length > 0)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ securityParameters.m_peerCertificate = serverCertificate;
+ securityParameters.m_tlsServerEndPoint = null;
+
+ TlsAuthentication authentication = client.GetAuthentication();
+ if (null == authentication)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return authentication;
+ }
+
+ public static bool ContainsNonAscii(byte[] bs)
+ {
+ for (int i = 0; i < bs.Length; ++i)
+ {
+ int c = bs[i];
+ if (c >= 0x80)
+ return true;
+ }
+ return false;
+ }
+
+ public static bool ContainsNonAscii(string s)
+ {
+ for (int i = 0; i < s.Length; ++i)
+ {
+ int c = s[i];
+ if (c >= 0x80)
+ return true;
+ }
+ return false;
+ }
+
+ internal static IDictionary AddEarlyKeySharesToClientHello(TlsClientContext clientContext, TlsClient client,
+ IDictionary clientExtensions)
+ {
+ /*
+ * RFC 8446 9.2. If containing a "supported_groups" extension, it MUST also contain a
+ * "key_share" extension, and vice versa. An empty KeyShare.client_shares vector is
+ * permitted.
+ */
+ if (!IsTlsV13(clientContext.ClientVersion)
+ || !clientExtensions.Contains(ExtensionType.supported_groups))
+ {
+ return null;
+ }
+
+ int[] supportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(clientExtensions);
+ IList keyShareGroups = client.GetEarlyKeyShareGroups();
+ IDictionary clientAgreements = Platform.CreateHashtable(3);
+ IList clientShares = Platform.CreateArrayList(2);
+
+ CollectKeyShares(clientContext.Crypto, supportedGroups, keyShareGroups, clientAgreements, clientShares);
+
+ TlsExtensionsUtilities.AddKeyShareClientHello(clientExtensions, clientShares);
+
+ return clientAgreements;
+ }
+
+ internal static IDictionary AddKeyShareToClientHelloRetry(TlsClientContext clientContext,
+ IDictionary clientExtensions, int keyShareGroup)
+ {
+ int[] supportedGroups = new int[]{ keyShareGroup };
+ IList keyShareGroups = VectorOfOne(keyShareGroup);
+ IDictionary clientAgreements = Platform.CreateHashtable(1);
+ IList clientShares = Platform.CreateArrayList(1);
+
+ CollectKeyShares(clientContext.Crypto, supportedGroups, keyShareGroups, clientAgreements, clientShares);
+
+ TlsExtensionsUtilities.AddKeyShareClientHello(clientExtensions, clientShares);
+
+ if (clientAgreements.Count < 1 || clientShares.Count < 1)
+ {
+ // NOTE: Probable cause is declaring an unsupported NamedGroup in supported_groups extension
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return clientAgreements;
+ }
+
+ private static void CollectKeyShares(TlsCrypto crypto, int[] supportedGroups, IList keyShareGroups,
+ IDictionary clientAgreements, IList clientShares)
+ {
+ if (IsNullOrEmpty(supportedGroups))
+ return;
+
+ if (null == keyShareGroups || keyShareGroups.Count < 1)
+ return;
+
+ for (int i = 0; i < supportedGroups.Length; ++i)
+ {
+ int supportedGroup = supportedGroups[i];
+
+ if (!keyShareGroups.Contains(supportedGroup)
+ || clientAgreements.Contains(supportedGroup)
+ || !crypto.HasNamedGroup(supportedGroup))
+ {
+ continue;
+ }
+
+ TlsAgreement agreement = null;
+ if (NamedGroup.RefersToASpecificCurve(supportedGroup))
+ {
+ if (crypto.HasECDHAgreement())
+ {
+ agreement = crypto.CreateECDomain(new TlsECConfig(supportedGroup)).CreateECDH();
+ }
+ }
+ else if (NamedGroup.RefersToASpecificFiniteField(supportedGroup))
+ {
+ if (crypto.HasDHAgreement())
+ {
+ agreement = crypto.CreateDHDomain(new TlsDHConfig(supportedGroup, true)).CreateDH();
+ }
+ }
+
+ if (null != agreement)
+ {
+ byte[] key_exchange = agreement.GenerateEphemeral();
+ KeyShareEntry clientShare = new KeyShareEntry(supportedGroup, key_exchange);
+
+ clientShares.Add(clientShare);
+ clientAgreements[supportedGroup] = agreement;
+ }
+ }
+ }
+
+ internal static KeyShareEntry SelectKeyShare(IList clientShares, int keyShareGroup)
+ {
+ if (null != clientShares && 1 == clientShares.Count)
+ {
+ KeyShareEntry clientShare = (KeyShareEntry)clientShares[0];
+ if (null != clientShare && clientShare.NamedGroup == keyShareGroup)
+ {
+ return clientShare;
+ }
+ }
+ return null;
+ }
+
+ internal static KeyShareEntry SelectKeyShare(TlsCrypto crypto, ProtocolVersion negotiatedVersion,
+ IList clientShares, int[] clientSupportedGroups, int[] serverSupportedGroups)
+ {
+ if (null != clientShares && !IsNullOrEmpty(clientSupportedGroups) && !IsNullOrEmpty(serverSupportedGroups))
+ {
+ foreach (KeyShareEntry clientShare in clientShares)
+ {
+ int group = clientShare.NamedGroup;
+
+ if (!NamedGroup.CanBeNegotiated(group, negotiatedVersion))
+ continue;
+
+ if (!Arrays.Contains(serverSupportedGroups, group) ||
+ !Arrays.Contains(clientSupportedGroups, group))
+ {
+ continue;
+ }
+
+ if (!crypto.HasNamedGroup(group))
+ continue;
+
+ if ((NamedGroup.RefersToASpecificCurve(group) && !crypto.HasECDHAgreement()) ||
+ (NamedGroup.RefersToASpecificFiniteField(group) && !crypto.HasDHAgreement()))
+ {
+ continue;
+ }
+
+ return clientShare;
+ }
+ }
+ return null;
+ }
+
+ internal static int SelectKeyShareGroup(TlsCrypto crypto, ProtocolVersion negotiatedVersion,
+ int[] clientSupportedGroups, int[] serverSupportedGroups)
+ {
+ if (!IsNullOrEmpty(clientSupportedGroups) && !IsNullOrEmpty(serverSupportedGroups))
+ {
+ foreach (int group in clientSupportedGroups)
+ {
+ if (!NamedGroup.CanBeNegotiated(group, negotiatedVersion))
+ continue;
+
+ if (!Arrays.Contains(serverSupportedGroups, group))
+ continue;
+
+ if (!crypto.HasNamedGroup(group))
+ continue;
+
+ if ((NamedGroup.RefersToASpecificCurve(group) && !crypto.HasECDHAgreement()) ||
+ (NamedGroup.RefersToASpecificFiniteField(group) && !crypto.HasDHAgreement()))
+ {
+ continue;
+ }
+
+ return group;
+ }
+ }
+ return -1;
+ }
+
+ internal static byte[] ReadEncryptedPms(TlsContext context, Stream input)
+ {
+ if (IsSsl(context))
+ return Ssl3Utilities.ReadEncryptedPms(input);
+
+ return ReadOpaque16(input);
+ }
+
+ internal static void WriteEncryptedPms(TlsContext context, byte[] encryptedPms, Stream output)
+ {
+ if (IsSsl(context))
+ {
+ Ssl3Utilities.WriteEncryptedPms(encryptedPms, output);
+ }
+ else
+ {
+ WriteOpaque16(encryptedPms, output);
+ }
+ }
+
+ internal static byte[] GetSessionID(TlsSession tlsSession)
+ {
+ if (null != tlsSession)
+ {
+ byte[] sessionID = tlsSession.SessionID;
+ if (null != sessionID
+ && sessionID.Length > 0
+ && sessionID.Length <= 32)
+ {
+ return sessionID;
+ }
+ }
+ return EmptyBytes;
+ }
+
+ internal static void AdjustTranscriptForRetry(TlsHandshakeHash handshakeHash)
+ {
+ byte[] clientHelloHash = GetCurrentPrfHash(handshakeHash);
+ handshakeHash.Reset();
+
+ int length = clientHelloHash.Length;
+ CheckUint8(length);
+
+ byte[] synthetic = new byte[4 + length];
+ WriteUint8(HandshakeType.message_hash, synthetic, 0);
+ WriteUint24(length, synthetic, 1);
+ Array.Copy(clientHelloHash, 0, synthetic, 4, length);
+
+ handshakeHash.Update(synthetic, 0, synthetic.Length);
+ }
+
+ internal static TlsCredentials EstablishClientCredentials(TlsAuthentication clientAuthentication,
+ CertificateRequest certificateRequest)
+ {
+ return ValidateCredentials(clientAuthentication.GetClientCredentials(certificateRequest));
+ }
+
+ internal static TlsCredentialedSigner Establish13ClientCredentials(TlsAuthentication clientAuthentication,
+ CertificateRequest certificateRequest)
+ {
+ return Validate13Credentials(clientAuthentication.GetClientCredentials(certificateRequest));
+ }
+
+ internal static void EstablishClientSigAlgs(SecurityParameters securityParameters,
+ IDictionary clientExtensions)
+ {
+ securityParameters.m_clientSigAlgs = TlsExtensionsUtilities.GetSignatureAlgorithmsExtension(
+ clientExtensions);
+ securityParameters.m_clientSigAlgsCert = TlsExtensionsUtilities.GetSignatureAlgorithmsCertExtension(
+ clientExtensions);
+ }
+
+ internal static TlsCredentials EstablishServerCredentials(TlsServer server)
+ {
+ return ValidateCredentials(server.GetCredentials());
+ }
+
+ internal static TlsCredentialedSigner Establish13ServerCredentials(TlsServer server)
+ {
+ return Validate13Credentials(server.GetCredentials());
+ }
+
+ internal static void EstablishServerSigAlgs(SecurityParameters securityParameters,
+ CertificateRequest certificateRequest)
+ {
+ securityParameters.m_clientCertTypes = certificateRequest.CertificateTypes;
+ securityParameters.m_serverSigAlgs = certificateRequest.SupportedSignatureAlgorithms;
+ securityParameters.m_serverSigAlgsCert = certificateRequest.SupportedSignatureAlgorithmsCert;
+
+ if (null == securityParameters.ServerSigAlgsCert)
+ {
+ securityParameters.m_serverSigAlgsCert = securityParameters.ServerSigAlgs;
+ }
+ }
+
+ internal static TlsCredentials ValidateCredentials(TlsCredentials credentials)
+ {
+ if (null != credentials)
+ {
+ int count = 0;
+ count += (credentials is TlsCredentialedAgreement) ? 1 : 0;
+ count += (credentials is TlsCredentialedDecryptor) ? 1 : 0;
+ count += (credentials is TlsCredentialedSigner) ? 1 : 0;
+ if (count != 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ return credentials;
+ }
+
+ internal static TlsCredentialedSigner Validate13Credentials(TlsCredentials credentials)
+ {
+ if (null == credentials)
+ return null;
+
+ if (!(credentials is TlsCredentialedSigner))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return (TlsCredentialedSigner)credentials;
+ }
+
+ internal static void NegotiatedCipherSuite(SecurityParameters securityParameters, int cipherSuite)
+ {
+ securityParameters.m_cipherSuite = cipherSuite;
+ securityParameters.m_keyExchangeAlgorithm = GetKeyExchangeAlgorithm(cipherSuite);
+
+ int prfAlgorithm = GetPrfAlgorithm(securityParameters, cipherSuite);
+ securityParameters.m_prfAlgorithm = prfAlgorithm;
+
+ switch (prfAlgorithm)
+ {
+ case PrfAlgorithm.ssl_prf_legacy:
+ case PrfAlgorithm.tls_prf_legacy:
+ {
+ securityParameters.m_prfHashAlgorithm = -1;
+ securityParameters.m_prfHashLength = -1;
+ break;
+ }
+ default:
+ {
+ short prfHashAlgorithm = GetHashAlgorithmForPrfAlgorithm(prfAlgorithm);
+
+ securityParameters.m_prfHashAlgorithm = prfHashAlgorithm;
+ securityParameters.m_prfHashLength = HashAlgorithm.GetOutputSize(prfHashAlgorithm);
+ break;
+ }
+ }
+
+ /*
+ * TODO[tls13] We're slowly moving towards negotiating cipherSuite THEN version. We could
+ * move this to "after parameter negotiation" i.e. after ServerHello/EncryptedExtensions.
+ */
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+ if (IsTlsV13(negotiatedVersion))
+ {
+ securityParameters.m_verifyDataLength = securityParameters.PrfHashLength;
+ }
+ else
+ {
+ securityParameters.m_verifyDataLength = negotiatedVersion.IsSsl ? 36 : 12;
+ }
+ }
+
+ internal static void NegotiatedVersion(SecurityParameters securityParameters)
+ {
+ if (!IsSignatureAlgorithmsExtensionAllowed(securityParameters.NegotiatedVersion))
+ {
+ securityParameters.m_clientSigAlgs = null;
+ securityParameters.m_clientSigAlgsCert = null;
+ return;
+ }
+
+ if (null == securityParameters.ClientSigAlgs)
+ {
+ securityParameters.m_clientSigAlgs = GetLegacySupportedSignatureAlgorithms();
+ }
+
+ if (null == securityParameters.ClientSigAlgsCert)
+ {
+ securityParameters.m_clientSigAlgsCert = securityParameters.ClientSigAlgs;
+ }
+ }
+
+ internal static void NegotiatedVersionDtlsClient(TlsClientContext clientContext, TlsClient client)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (!ProtocolVersion.IsSupportedDtlsVersionClient(negotiatedVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ NegotiatedVersion(securityParameters);
+
+ client.NotifyServerVersion(negotiatedVersion);
+ }
+
+ internal static void NegotiatedVersionDtlsServer(TlsServerContext serverContext)
+ {
+ SecurityParameters securityParameters = serverContext.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (!ProtocolVersion.IsSupportedDtlsVersionServer(negotiatedVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ NegotiatedVersion(securityParameters);
+ }
+
+ internal static void NegotiatedVersionTlsClient(TlsClientContext clientContext, TlsClient client)
+ {
+ SecurityParameters securityParameters = clientContext.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (!ProtocolVersion.IsSupportedTlsVersionClient(negotiatedVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ NegotiatedVersion(securityParameters);
+
+ client.NotifyServerVersion(negotiatedVersion);
+ }
+
+ internal static void NegotiatedVersionTlsServer(TlsServerContext serverContext)
+ {
+ SecurityParameters securityParameters = serverContext.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (!ProtocolVersion.IsSupportedTlsVersionServer(negotiatedVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ NegotiatedVersion(securityParameters);
+ }
+
+ internal static TlsSecret DeriveSecret(SecurityParameters securityParameters, TlsSecret secret, string label,
+ byte[] transcriptHash)
+ {
+ return TlsCryptoUtilities.HkdfExpandLabel(secret, securityParameters.PrfHashAlgorithm, label,
+ transcriptHash, securityParameters.PrfHashLength);
+ }
+
+ internal static TlsSecret GetSessionMasterSecret(TlsCrypto crypto, TlsSecret masterSecret)
+ {
+ if (null != masterSecret)
+ {
+ lock (masterSecret)
+ {
+ if (masterSecret.IsAlive())
+ return crypto.AdoptSecret(masterSecret);
+ }
+ }
+
+ return null;
+ }
+
+ internal static bool IsPermittedExtensionType13(int handshakeType, int extensionType)
+ {
+ switch (extensionType)
+ {
+ case ExtensionType.server_name:
+ case ExtensionType.max_fragment_length:
+ case ExtensionType.supported_groups:
+ case ExtensionType.use_srtp:
+ case ExtensionType.heartbeat:
+ case ExtensionType.application_layer_protocol_negotiation:
+ case ExtensionType.client_certificate_type:
+ case ExtensionType.server_certificate_type:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.encrypted_extensions:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.status_request:
+ case ExtensionType.signed_certificate_timestamp:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.certificate_request:
+ case HandshakeType.certificate:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.signature_algorithms:
+ case ExtensionType.certificate_authorities:
+ case ExtensionType.signature_algorithms_cert:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.certificate_request:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.padding:
+ case ExtensionType.psk_key_exchange_modes:
+ case ExtensionType.post_handshake_auth:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.key_share:
+ case ExtensionType.supported_versions:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.server_hello:
+ case HandshakeType.hello_retry_request:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.pre_shared_key:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.server_hello:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.early_data:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.encrypted_extensions:
+ case HandshakeType.new_session_ticket:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.cookie:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.client_hello:
+ case HandshakeType.hello_retry_request:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ExtensionType.oid_filters:
+ {
+ switch (handshakeType)
+ {
+ case HandshakeType.certificate_request:
+ return true;
+ default:
+ return false;
+ }
+ }
+ default:
+ {
+ return !ExtensionType.IsRecognized(extensionType);
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ internal static void CheckExtensionData13(IDictionary extensions, int handshakeType, short alertDescription)
+ {
+ foreach (int extensionType in extensions.Keys)
+ {
+ if (!IsPermittedExtensionType13(handshakeType, extensionType))
+ throw new TlsFatalAlert(alertDescription, "Invalid extension: "
+ + ExtensionType.GetText(extensionType));
+ }
+ }
+
+#if !PORTABLE || DOTNET
+ public static bool IsTimeout(SocketException e)
+ {
+#if NET_1_1
+ return 10060 == e.ErrorCode;
+#else
+ return SocketError.TimedOut == e.SocketErrorCode;
+#endif
+ }
+#endif
+ }
+}
diff --git a/crypto/src/tls/TrustedAuthority.cs b/crypto/src/tls/TrustedAuthority.cs
new file mode 100644
index 000000000..cd564ebfa
--- /dev/null
+++ b/crypto/src/tls/TrustedAuthority.cs
@@ -0,0 +1,149 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ public sealed class TrustedAuthority
+ {
+ private readonly short m_identifierType;
+ private readonly object m_identifier;
+
+ public TrustedAuthority(short identifierType, object identifier)
+ {
+ if (!IsCorrectType(identifierType, identifier))
+ throw new ArgumentException("not an instance of the correct type", "identifier");
+
+ this.m_identifierType = identifierType;
+ this.m_identifier = identifier;
+ }
+
+ public short IdentifierType
+ {
+ get { return m_identifierType; }
+ }
+
+ public object Identifier
+ {
+ get { return m_identifier; }
+ }
+
+ public byte[] GetCertSha1Hash()
+ {
+ return Arrays.Clone((byte[])m_identifier);
+ }
+
+ public byte[] GetKeySha1Hash()
+ {
+ return Arrays.Clone((byte[])m_identifier);
+ }
+
+ public X509Name X509Name
+ {
+ get
+ {
+ CheckCorrectType(Tls.IdentifierType.x509_name);
+ return (X509Name)m_identifier;
+ }
+ }
+
+ /// <summary>Encode this <see cref="TrustedAuthority"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(m_identifierType, output);
+
+ switch (m_identifierType)
+ {
+ case Tls.IdentifierType.cert_sha1_hash:
+ case Tls.IdentifierType.key_sha1_hash:
+ {
+ byte[] sha1Hash = (byte[])m_identifier;
+ output.Write(sha1Hash, 0, sha1Hash.Length);
+ break;
+ }
+ case Tls.IdentifierType.pre_agreed:
+ {
+ break;
+ }
+ case Tls.IdentifierType.x509_name:
+ {
+ X509Name dn = (X509Name)m_identifier;
+ byte[] derEncoding = dn.GetEncoded(Asn1Encodable.Der);
+ TlsUtilities.WriteOpaque16(derEncoding, output);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /// <summary>Parse a <see cref="TrustedAuthority"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="TrustedAuthority"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static TrustedAuthority Parse(Stream input)
+ {
+ short identifier_type = TlsUtilities.ReadUint8(input);
+ object identifier;
+
+ switch (identifier_type)
+ {
+ case Tls.IdentifierType.cert_sha1_hash:
+ case Tls.IdentifierType.key_sha1_hash:
+ {
+ identifier = TlsUtilities.ReadFully(20, input);
+ break;
+ }
+ case Tls.IdentifierType.pre_agreed:
+ {
+ identifier = null;
+ break;
+ }
+ case Tls.IdentifierType.x509_name:
+ {
+ byte[] derEncoding = TlsUtilities.ReadOpaque16(input, 1);
+ Asn1Object asn1 = TlsUtilities.ReadDerObject(derEncoding);
+ identifier = X509Name.GetInstance(asn1);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new TrustedAuthority(identifier_type, identifier);
+ }
+
+ private void CheckCorrectType(short expectedIdentifierType)
+ {
+ if (m_identifierType != expectedIdentifierType || !IsCorrectType(expectedIdentifierType, m_identifier))
+ throw new InvalidOperationException("TrustedAuthority is not of type "
+ + Tls.IdentifierType.GetName(expectedIdentifierType));
+ }
+
+ private static bool IsCorrectType(short identifierType, object identifier)
+ {
+ switch (identifierType)
+ {
+ case Tls.IdentifierType.cert_sha1_hash:
+ case Tls.IdentifierType.key_sha1_hash:
+ return IsSha1Hash(identifier);
+ case Tls.IdentifierType.pre_agreed:
+ return identifier == null;
+ case Tls.IdentifierType.x509_name:
+ return identifier is X509Name;
+ default:
+ throw new ArgumentException("unsupported IdentifierType", "identifierType");
+ }
+ }
+
+ private static bool IsSha1Hash(object identifier)
+ {
+ return identifier is byte[] && ((byte[])identifier).Length == 20;
+ }
+ }
+}
diff --git a/crypto/src/tls/UrlAndHash.cs b/crypto/src/tls/UrlAndHash.cs
new file mode 100644
index 000000000..47347c185
--- /dev/null
+++ b/crypto/src/tls/UrlAndHash.cs
@@ -0,0 +1,83 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 6066 5.</summary>
+ public sealed class UrlAndHash
+ {
+ private readonly string m_url;
+ private readonly byte[] m_sha1Hash;
+
+ public UrlAndHash(string url, byte[] sha1Hash)
+ {
+ if (TlsUtilities.IsNullOrEmpty(url) || 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.m_url = url;
+ this.m_sha1Hash = sha1Hash;
+ }
+
+ public string Url
+ {
+ get { return m_url; }
+ }
+
+ public byte[] Sha1Hash
+ {
+ get { return m_sha1Hash; }
+ }
+
+ /// <summary>Encode this <see cref="UrlAndHash"/> to a <see cref="Stream"/>.</summary>
+ /// <param name="output">the <see cref="Stream"/> to encode to.</param>
+ /// <exception cref="IOException"/>
+ public void Encode(Stream output)
+ {
+ byte[] urlEncoding = Strings.ToByteArray(m_url);
+ TlsUtilities.WriteOpaque16(urlEncoding, output);
+
+ if (m_sha1Hash == null)
+ {
+ TlsUtilities.WriteUint8(0, output);
+ }
+ else
+ {
+ TlsUtilities.WriteUint8(1, output);
+ output.Write(m_sha1Hash, 0, m_sha1Hash.Length);
+ }
+ }
+
+ /// <summary>Parse a <see cref="UrlAndHash"/> from a <see cref="Stream"/>.</summary>
+ /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
+ /// <param name="input">the <see cref="Stream"/> to parse from.</param>
+ /// <returns>a <see cref="UrlAndHash"/> object.</returns>
+ /// <exception cref="IOException"/>
+ public static UrlAndHash Parse(TlsContext context, Stream input)
+ {
+ byte[] urlEncoding = TlsUtilities.ReadOpaque16(input, 1);
+ string url = Strings.FromByteArray(urlEncoding);
+
+ byte[] sha1Hash = null;
+ short 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/tls/UseSrtpData.cs b/crypto/src/tls/UseSrtpData.cs
new file mode 100644
index 000000000..6c7e7da23
--- /dev/null
+++ b/crypto/src/tls/UseSrtpData.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 5764 4.1.1</summary>
+ public sealed class UseSrtpData
+ {
+ private readonly int[] m_protectionProfiles;
+ private readonly byte[] m_mki;
+
+ /// <param name="protectionProfiles">see <see cref="SrtpProtectionProfile"/> for valid constants.</param>
+ /// <param name="mki">valid lengths from 0 to 255.</param>
+ public UseSrtpData(int[] protectionProfiles, byte[] mki)
+ {
+ if (TlsUtilities.IsNullOrEmpty(protectionProfiles) || 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.m_protectionProfiles = protectionProfiles;
+ this.m_mki = mki;
+ }
+
+ /// <returns>see <see cref="SrtpProtectionProfile"/> for valid constants.</returns>
+ public int[] ProtectionProfiles
+ {
+ get { return m_protectionProfiles; }
+ }
+
+ /// <returns>valid lengths from 0 to 255.</returns>
+ public byte[] Mki
+ {
+ get { return m_mki; }
+ }
+ }
+}
diff --git a/crypto/src/tls/UserMappingType.cs b/crypto/src/tls/UserMappingType.cs
new file mode 100644
index 000000000..ba02cbc5b
--- /dev/null
+++ b/crypto/src/tls/UserMappingType.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /// <summary>RFC 4681</summary>
+ public abstract class UserMappingType
+ {
+ /*
+ * RFC 4681
+ */
+ public const short upn_domain_hint = 64;
+ }
+}
diff --git a/crypto/src/tls/crypto/CryptoHashAlgorithm.cs b/crypto/src/tls/crypto/CryptoHashAlgorithm.cs
new file mode 100644
index 000000000..dc3c86956
--- /dev/null
+++ b/crypto/src/tls/crypto/CryptoHashAlgorithm.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public abstract class CryptoHashAlgorithm
+ {
+ public const int md5 = 1;
+ public const int sha1 = 2;
+ public const int sha224 = 3;
+ public const int sha256 = 4;
+ public const int sha384 = 5;
+ public const int sha512 = 6;
+ public const int sm3 = 7;
+ }
+}
diff --git a/crypto/src/tls/crypto/CryptoSignatureAlgorithm.cs b/crypto/src/tls/crypto/CryptoSignatureAlgorithm.cs
new file mode 100644
index 000000000..ed58820b8
--- /dev/null
+++ b/crypto/src/tls/crypto/CryptoSignatureAlgorithm.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public abstract class CryptoSignatureAlgorithm
+ {
+ public const int rsa = 1;
+ public const int dsa = 2;
+ public const int ecdsa = 3;
+ public const int rsa_pss_rsae_sha256 = 4;
+ public const int rsa_pss_rsae_sha384 = 5;
+ public const int rsa_pss_rsae_sha512 = 6;
+ public const int ed25519 = 7;
+ public const int ed448 = 8;
+ public const int rsa_pss_pss_sha256 = 9;
+ public const int rsa_pss_pss_sha384 = 10;
+ public const int rsa_pss_pss_sha512 = 11;
+ public const int gostr34102012_256 = 64;
+ public const int gostr34102012_512 = 65;
+ public const int sm2 = 200;
+ }
+}
diff --git a/crypto/src/tls/crypto/DHGroup.cs b/crypto/src/tls/crypto/DHGroup.cs
new file mode 100644
index 000000000..364a60429
--- /dev/null
+++ b/crypto/src/tls/crypto/DHGroup.cs
@@ -0,0 +1,46 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Carrier class for Diffie-Hellman group parameters.</summary>
+ public class DHGroup
+ {
+ private readonly BigInteger g, p, q;
+ private readonly int l;
+
+ /// <summary>Base constructor with the prime factor of (p - 1).</summary>
+ /// <param name="p">the prime modulus.</param>
+ /// <param name="q">specifies the prime factor of (p - 1).</param>
+ /// <param name="g">the base generator.</param>
+ /// <param name="l"></param>
+ public DHGroup(BigInteger p, BigInteger q, BigInteger g, int l)
+ {
+ this.p = p;
+ this.g = g;
+ this.q = q;
+ this.l = l;
+ }
+
+ public virtual BigInteger G
+ {
+ get { return g; }
+ }
+
+ public virtual int L
+ {
+ get { return l; }
+ }
+
+ public virtual BigInteger P
+ {
+ get { return p; }
+ }
+
+ public virtual BigInteger Q
+ {
+ get { return q; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/DHStandardGroups.cs b/crypto/src/tls/crypto/DHStandardGroups.cs
new file mode 100644
index 000000000..40ddb5725
--- /dev/null
+++ b/crypto/src/tls/crypto/DHStandardGroups.cs
@@ -0,0 +1,248 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Standard Diffie-Hellman groups from various IETF specifications.</summary>
+ public class DHStandardGroups
+ {
+ private static readonly BigInteger Two = BigInteger.Two;
+
+ private static BigInteger FromHex(string hex)
+ {
+ return new BigInteger(1, Hex.DecodeStrict(hex));
+ }
+
+ //private static DHGroup FromPG(string hexP, string hexG)
+ //{
+ // return new DHGroup(FromHex(hexP), null, FromHex(hexG), 0);
+ //}
+
+ private static DHGroup SafePrimeGen2(string hexP)
+ {
+ return SafePrimeGen2(hexP, 0);
+ }
+
+ private static DHGroup SafePrimeGen2(string hexP, int l)
+ {
+ // NOTE: A group using a safe prime (i.e. q = (p-1)/2), and generator g = 2
+ BigInteger p = FromHex(hexP);
+ return new DHGroup(p, p.ShiftRight(1), Two, l);
+ }
+
+ /*
+ * RFC 2409
+ */
+ private static readonly string rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF";
+ public static readonly DHGroup rfc2409_768 = SafePrimeGen2(rfc2409_768_p);
+
+ private static readonly string rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ + "FFFFFFFFFFFFFFFF";
+ public static readonly DHGroup rfc2409_1024 = SafePrimeGen2(rfc2409_1024_p);
+
+ /*
+ * RFC 3526
+ */
+ private static readonly string rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+ private static readonly int rfc3526_1536_l = 200; // RFC3526/RFC7919
+ public static readonly DHGroup rfc3526_1536 = SafePrimeGen2(rfc3526_1536_p, rfc3526_1536_l);
+
+ private static readonly string rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF";
+ private static readonly int rfc3526_2048_l = System.Math.Max(225, 112 * 2); // MAX(RFC3526/RFC7919,FIPS)
+ public static readonly DHGroup rfc3526_2048 = SafePrimeGen2(rfc3526_2048_p, rfc3526_2048_l);
+
+ 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 int rfc3526_3072_l = System.Math.Max(275, 128 * 2); // MAX(RFC3526/RFC7919,FIPS)
+ public static readonly DHGroup rfc3526_3072 = SafePrimeGen2(rfc3526_3072_p, rfc3526_3072_l);
+
+ 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 int rfc3526_4096_l = System.Math.Max(325, 152 * 2); // MAX(RFC3526/RFC7919,FIPS)
+ public static readonly DHGroup rfc3526_4096 = SafePrimeGen2(rfc3526_4096_p, rfc3526_4096_l);
+
+ 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 int rfc3526_6144_l = System.Math.Max(375, 176 * 2); // MAX(RFC3526/RFC7919,FIPS)
+ public static readonly DHGroup rfc3526_6144 = SafePrimeGen2(rfc3526_6144_p, rfc3526_6144_l);
+
+ 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 int rfc3526_8192_l = System.Math.Max(400, 200 * 2); // MAX(RFC3526/RFC7919,FIPS)
+ public static readonly DHGroup rfc3526_8192 = SafePrimeGen2(rfc3526_8192_p, rfc3526_8192_l);
+
+ /*
+ * RFC 4306
+ */
+ public static readonly DHGroup rfc4306_768 = rfc2409_768;
+ public static readonly DHGroup rfc4306_1024 = rfc2409_1024;
+
+ /*
+ * RFC 5996
+ */
+ public static readonly DHGroup rfc5996_768 = rfc4306_768;
+ public static readonly DHGroup rfc5996_1024 = rfc4306_1024;
+
+ /*
+ * RFC 7919
+ */
+ private static readonly string rfc7919_ffdhe2048_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B423861285C97FFFFFFFFFFFFFFFF";
+ private static readonly int rfc7919_ffdhe2048_l = System.Math.Max(225, 112 * 2); // MAX(RFC7919,FIPS)
+ public static readonly DHGroup rfc7919_ffdhe2048 = SafePrimeGen2(rfc7919_ffdhe2048_p, rfc7919_ffdhe2048_l);
+
+ private static readonly string rfc7919_ffdhe3072_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF";
+ private static readonly int rfc7919_ffdhe3072_l = System.Math.Max(275, 128 * 2); // MAX(RFC7919,FIPS)
+ public static readonly DHGroup rfc7919_ffdhe3072 = SafePrimeGen2(rfc7919_ffdhe3072_p, rfc7919_ffdhe3072_l);
+
+ private static readonly string rfc7919_ffdhe4096_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A"
+ + "FFFFFFFFFFFFFFFF";
+ private static readonly int rfc7919_ffdhe4096_l = System.Math.Max(325, 152 * 2); // MAX(RFC7919,FIPS)
+ public static readonly DHGroup rfc7919_ffdhe4096 = SafePrimeGen2(rfc7919_ffdhe4096_p, rfc7919_ffdhe4096_l);
+
+ private static readonly string rfc7919_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";
+ private static readonly int rfc7919_ffdhe6144_l = System.Math.Max(375, 176 * 2); // MAX(RFC7919,FIPS)
+ public static readonly DHGroup rfc7919_ffdhe6144 = SafePrimeGen2(rfc7919_ffdhe6144_p, rfc7919_ffdhe6144_l);
+
+ private static readonly string rfc7919_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";
+ private static readonly int rfc7919_ffdhe8192_l = System.Math.Max(400, 200 * 2); // MAX(RFC7919,FIPS)
+ public static readonly DHGroup rfc7919_ffdhe8192 = SafePrimeGen2(rfc7919_ffdhe8192_p, rfc7919_ffdhe8192_l);
+ }
+}
diff --git a/crypto/src/tls/crypto/Srp6Group.cs b/crypto/src/tls/crypto/Srp6Group.cs
new file mode 100644
index 000000000..dae4ca1d7
--- /dev/null
+++ b/crypto/src/tls/crypto/Srp6Group.cs
@@ -0,0 +1,31 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Carrier class for SRP-6 group parameters.</summary>
+ public class Srp6Group
+ {
+ private readonly BigInteger n, g;
+
+ /// <summary>Base constructor.</summary>
+ /// <param name="n">the n value.</param>
+ /// <param name="g">the g value.</param>
+ public Srp6Group(BigInteger n, BigInteger g)
+ {
+ this.n = n;
+ this.g = g;
+ }
+
+ public virtual BigInteger G
+ {
+ get { return g; }
+ }
+
+ public virtual BigInteger N
+ {
+ get { return n; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/Srp6StandardGroups.cs b/crypto/src/tls/crypto/Srp6StandardGroups.cs
new file mode 100644
index 000000000..371079cc3
--- /dev/null
+++ b/crypto/src/tls/crypto/Srp6StandardGroups.cs
@@ -0,0 +1,159 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>A selection of standard groups for SRP-6.</summary>
+ public class Srp6StandardGroups
+ {
+ private static BigInteger FromHex(string hex)
+ {
+ return new BigInteger(1, Hex.DecodeStrict(hex));
+ }
+
+ private static Srp6Group FromNG(string hexN, string hexG)
+ {
+ return new Srp6Group(FromHex(hexN), FromHex(hexG));
+ }
+
+ /*
+ * RFC 5054
+ */
+ private static readonly string rfc5054_1024_N = "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
+ + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
+ + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
+ + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3";
+ private static readonly string rfc5054_1024_g = "02";
+ public static readonly Srp6Group rfc5054_1024 = FromNG(rfc5054_1024_N, rfc5054_1024_g);
+
+ private static readonly string rfc5054_1536_N = "9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA961"
+ + "4B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F843"
+ + "80B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0B"
+ + "E3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF5"
+ + "6EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734A"
+ + "F7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E"
+ + "8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB";
+ private static readonly string rfc5054_1536_g = "02";
+ public static readonly Srp6Group rfc5054_1536 = FromNG(rfc5054_1536_N, rfc5054_1536_g);
+
+ private static readonly string rfc5054_2048_N = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294"
+ + "3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D"
+ + "CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB"
+ + "D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74"
+ + "7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A"
+ + "436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D"
+ + "5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73"
+ + "03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6"
+ + "94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F" + "9E4AFF73";
+ private static readonly string rfc5054_2048_g = "02";
+ public static readonly Srp6Group rfc5054_2048 = FromNG(rfc5054_2048_N, rfc5054_2048_g);
+
+ private static readonly string rfc5054_3072_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
+ private static readonly string rfc5054_3072_g = "05";
+ public static readonly Srp6Group rfc5054_3072 = FromNG(rfc5054_3072_N, rfc5054_3072_g);
+
+ private static readonly string rfc5054_4096_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF";
+ private static readonly string rfc5054_4096_g = "05";
+ public static readonly Srp6Group rfc5054_4096 = FromNG(rfc5054_4096_N, rfc5054_4096_g);
+
+ private static readonly string rfc5054_6144_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF";
+ private static readonly string rfc5054_6144_g = "05";
+ public static readonly Srp6Group rfc5054_6144 = FromNG(rfc5054_6144_N, rfc5054_6144_g);
+
+ private static readonly string rfc5054_8192_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ + "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
+ + "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
+ + "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
+ + "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
+ + "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
+ + "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
+ + "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
+ + "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
+ private static readonly string rfc5054_8192_g = "13";
+ public static readonly Srp6Group rfc5054_8192 = FromNG(rfc5054_8192_N, rfc5054_8192_g);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsAgreement.cs b/crypto/src/tls/crypto/TlsAgreement.cs
new file mode 100644
index 000000000..0635b4e9e
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsAgreement.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Base interface for ephemeral key agreement calculator.</summary>
+ public interface TlsAgreement
+ {
+ /// <summary>Generate an ephemeral key pair, returning the encoding of the public key.</summary>
+ /// <returns>a byte encoding of the public key.</returns>
+ /// <exception cref="IOException"/>
+ byte[] GenerateEphemeral();
+
+ /// <summary>Pass in the public key for the peer to the agreement calculator.</summary>
+ /// <param name="peerValue">a byte encoding of the peer public key.</param>
+ /// <exception cref="IOException"/>
+ void ReceivePeerValue(byte[] peerValue);
+
+ /// <summary>Calculate the agreed secret based on the calculator's current state.</summary>
+ /// <returns>the calculated secret.</returns>
+ /// <exception cref="IOException"/>
+ TlsSecret CalculateSecret();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCertificate.cs b/crypto/src/tls/crypto/TlsCertificate.cs
new file mode 100644
index 000000000..7bd8e0359
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCertificate.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Interface providing the functional representation of a single X.509 certificate.</summary>
+ public interface TlsCertificate
+ {
+ /// <param name="signatureAlgorithm"><see cref="SignatureAlgorithm"/></param>
+ /// <exception cref="IOException"/>
+ TlsVerifier CreateVerifier(short signatureAlgorithm);
+
+ /// <param name="signatureScheme"><see cref="SignatureScheme"/></param>
+ /// <exception cref="IOException"/>
+ TlsVerifier CreateVerifier(int signatureScheme);
+
+ /// <exception cref="IOException"/>
+ byte[] GetEncoded();
+
+ /// <exception cref="IOException"/>
+ byte[] GetExtension(DerObjectIdentifier extensionOid);
+
+ BigInteger SerialNumber { get; }
+
+ /// <returns>the OID of this certificate's 'signatureAlgorithm', as a string.</returns>
+ string SigAlgOid { get; }
+
+ /// <exception cref="IOException"/>
+ Asn1Encodable GetSigAlgParams();
+
+ /// <returns><see cref="SignatureAlgorithm"/></returns>
+ /// <exception cref="IOException"/>
+ short GetLegacySignatureAlgorithm();
+
+ /// <param name="signatureAlgorithm"><see cref="SignatureAlgorithm"/></param>
+ /// <returns>true if (and only if) this certificate can be used to verify the given signature algorithm.
+ /// </returns>
+ /// <exception cref="IOException"/>
+ bool SupportsSignatureAlgorithm(short signatureAlgorithm);
+
+ /// <exception cref="IOException"/>
+ bool SupportsSignatureAlgorithmCA(short signatureAlgorithm);
+
+ /// <param name="connectionEnd"><see cref="ConnectionEnd"/></param>
+ /// <param name="tlsCertificateRole"><see cref="TlsCertificateRole"/></param>
+ /// <exception cref="IOException"/>
+ TlsCertificate CheckUsageInRole(int connectionEnd, int tlsCertificateRole);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCertificateRole.cs b/crypto/src/tls/crypto/TlsCertificateRole.cs
new file mode 100644
index 000000000..f7d81b19e
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCertificateRole.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public abstract class TlsCertificateRole
+ {
+ public const int DH = 1;
+ public const int ECDH = 2;
+ public const int RsaEncryption = 3;
+ public const int Sm2Encryption = 4;
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCipher.cs b/crypto/src/tls/crypto/TlsCipher.cs
new file mode 100644
index 000000000..4c2147bf7
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCipher.cs
@@ -0,0 +1,61 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Base interface for a TLS bulk cipher.</summary>
+ public interface TlsCipher
+ {
+ /// <summary>Return the maximum input size for a ciphertext given a maximum output size for the plaintext of
+ /// plaintextLimit bytes.</summary>
+ /// <param name="plaintextLimit">the maximum output size for the plaintext.</param>
+ /// <returns>the maximum input size of the ciphertext for plaintextlimit bytes of output.</returns>
+ int GetCiphertextDecodeLimit(int plaintextLimit);
+
+ /// <summary>Return the maximum output size for a ciphertext given an actual input plaintext size of
+ /// plaintextLength bytes and a maximum input plaintext size of plaintextLimit bytes.</summary>
+ /// <param name="plaintextLength">the actual input size for the plaintext.</param>
+ /// <param name="plaintextLimit">the maximum input size for the plaintext.</param>
+ /// <returns>the maximum output size of the ciphertext for plaintextlimit bytes of input.</returns>
+ int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit);
+
+ /// <summary>Return the maximum size for the plaintext given ciphertextlimit bytes of ciphertext.</summary>
+ /// <param name="ciphertextLimit">the maximum number of bytes of ciphertext.</param>
+ /// <returns>the maximum size of the plaintext for ciphertextlimit bytes of input.</returns>
+ int GetPlaintextLimit(int ciphertextLimit);
+
+ /// <summary>Encode the passed in plaintext using the current bulk cipher.</summary>
+ /// <param name="seqNo">sequence number of the message represented by plaintext.</param>
+ /// <param name="contentType">content type of the message represented by plaintext.</param>
+ /// <param name="recordVersion"><see cref="ProtocolVersion"/> used for the record.</param>
+ /// <param name="headerAllocation">extra bytes to allocate at start of returned byte array.</param>
+ /// <param name="plaintext">array holding input plaintext to the cipher.</param>
+ /// <param name="offset">offset into input array the plaintext starts at.</param>
+ /// <param name="len">length of the plaintext in the array.</param>
+ /// <returns>A <see cref="TlsEncodeResult"/> containing the result of encoding (after 'headerAllocation' unused
+ /// bytes).</returns>
+ /// <exception cref="IOException"/>
+ TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+ int headerAllocation, byte[] plaintext, int offset, int len);
+
+ /// <summary>Decode the passed in ciphertext using the current bulk cipher.</summary>
+ /// <param name="seqNo">sequence number of the message represented by ciphertext.</param>
+ /// <param name="recordType">content type used in the record for this message.</param>
+ /// <param name="recordVersion"><see cref="ProtocolVersion"/> used for the record.</param>
+ /// <param name="ciphertext">array holding input ciphertext to the cipher.</param>
+ /// <param name="offset">offset into input array the ciphertext starts at.</param>
+ /// <param name="len">length of the ciphertext in the array.</param>
+ /// <returns>A <see cref="TlsDecodeResult"/> containing the result of decoding.</returns>
+ /// <exception cref="IOException"/>
+ TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
+ byte[] ciphertext, int offset, int len);
+
+ /// <exception cref="IOException"/>
+ void RekeyDecoder();
+
+ /// <exception cref="IOException"/>
+ void RekeyEncoder();
+
+ bool UsesOpaqueRecordType { get; }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCrypto.cs b/crypto/src/tls/crypto/TlsCrypto.cs
new file mode 100644
index 000000000..bd003aefa
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCrypto.cs
@@ -0,0 +1,181 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Service and object creation interface for the primitive types and services that are associated with
+ /// cryptography in the API.</summary>
+ public interface TlsCrypto
+ {
+ /// <summary>Return true if this TlsCrypto can perform raw signatures and verifications for all supported
+ /// algorithms.</summary>
+ /// <returns>true if this instance can perform raw signatures and verifications for all supported algorithms,
+ /// false otherwise.</returns>
+ bool HasAllRawSignatureAlgorithms();
+
+ /// <summary>Return true if this TlsCrypto can support DH key agreement.</summary>
+ /// <returns>true if this instance can support DH key agreement, false otherwise.</returns>
+ bool HasDHAgreement();
+
+ /// <summary>Return true if this TlsCrypto can support ECDH key agreement.</summary>
+ /// <returns>true if this instance can support ECDH key agreement, false otherwise.</returns>
+ bool HasECDHAgreement();
+
+ /// <summary>Return true if this TlsCrypto can support the passed in block/stream encryption algorithm.
+ /// </summary>
+ /// <param name="encryptionAlgorithm">the algorithm of interest.</param>
+ /// <returns>true if encryptionAlgorithm is supported, false otherwise.</returns>
+ bool HasEncryptionAlgorithm(int encryptionAlgorithm);
+
+ /// <summary>Return true if this TlsCrypto can support the passed in hash algorithm.</summary>
+ /// <param name="cryptoHashAlgorithm">the algorithm of interest.</param>
+ /// <returns>true if cryptoHashAlgorithm is supported, false otherwise.</returns>
+ bool HasCryptoHashAlgorithm(int cryptoHashAlgorithm);
+
+ /// <summary>Return true if this TlsCrypto can support the passed in signature algorithm (not necessarily in
+ /// combination with EVERY hash algorithm).</summary>
+ /// <param name="cryptoSignatureAlgorithm">the algorithm of interest.</param>
+ /// <returns>true if cryptoSignatureAlgorithm is supported, false otherwise.</returns>
+ bool HasCryptoSignatureAlgorithm(int cryptoSignatureAlgorithm);
+
+ /// <summary>Return true if this TlsCrypto can support the passed in MAC algorithm.</summary>
+ /// <param name="macAlgorithm">the algorithm of interest.</param>
+ /// <returns>true if macAlgorithm is supported, false otherwise.</returns>
+ bool HasMacAlgorithm(int macAlgorithm);
+
+ /// <summary>Return true if this TlsCrypto supports the passed in <see cref="NamedGroup">named group</see>
+ /// value.</summary>
+ /// <returns>true if this instance supports the passed in <see cref="NamedGroup">named group</see> value.
+ /// </returns>
+ bool HasNamedGroup(int namedGroup);
+
+ /// <summary>Return true if this TlsCrypto can support RSA encryption/decryption.</summary>
+ /// <returns>true if this instance can support RSA encryption/decryption, false otherwise.</returns>
+ bool HasRsaEncryption();
+
+ /// <summary>Return true if this TlsCrypto can support the passed in signature algorithm (not necessarily in
+ /// combination with EVERY hash algorithm).</summary>
+ /// <returns>true if signatureAlgorithm is supported, false otherwise.</returns>
+ bool HasSignatureAlgorithm(short signatureAlgorithm);
+
+ /// <summary>Return true if this TlsCrypto can support the passed in signature algorithm.</summary>
+ /// <param name="sigAndHashAlgorithm">the algorithm of interest.</param>
+ /// <returns>true if sigAndHashAlgorithm is supported, false otherwise.</returns>
+ bool HasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm);
+
+ /// <summary>Return true if this TlsCrypto can support the passed in signature scheme.</summary>
+ /// <param name="signatureScheme">the scheme of interest.</param>
+ /// <returns>true if signatureScheme is supported, false otherwise.</returns>
+ bool HasSignatureScheme(int signatureScheme);
+
+ /// <summary>Return true if this TlsCrypto can support SRP authentication.</summary>
+ /// <returns>true if this instance can support SRP authentication, false otherwise.</returns>
+ bool HasSrpAuthentication();
+
+ /// <summary>Create a TlsSecret object based on provided data.</summary>
+ /// <param name="data">the data to base the TlsSecret on.</param>
+ /// <returns>a TlsSecret based on the provided data.</returns>
+ TlsSecret CreateSecret(byte[] data);
+
+ /// <summary>Create a TlsSecret object containing a randomly-generated RSA PreMasterSecret</summary>
+ /// <param name="clientVersion">the client version to place in the first 2 bytes</param>
+ /// <returns>a TlsSecret containing the PreMasterSecret.</returns>
+ TlsSecret GenerateRsaPreMasterSecret(ProtocolVersion clientVersion);
+
+ /// <summary>Return the primary (safest) SecureRandom for this crypto.</summary>
+ /// <returns>a SecureRandom suitable for key generation.</returns>
+ SecureRandom SecureRandom { get; }
+
+ /// <summary>Create a TlsCertificate from an ASN.1 binary encoding of an X.509 certificate.</summary>
+ /// <param name="encoding">DER/BER encoding of the certificate of interest.</param>
+ /// <returns>a TlsCertificate.</returns>
+ /// <exception cref="IOException">if there is an issue on decoding or constructing the certificate.</exception>
+ TlsCertificate CreateCertificate(byte[] encoding);
+
+ /// <summary>Create a cipher for the specified encryption and MAC algorithms.</summary>
+ /// <remarks>
+ /// See enumeration classes <see cref="EncryptionAlgorithm"/>, <see cref="MacAlgorithm"/> for appropriate
+ /// argument values.
+ /// </remarks>
+ /// <param name="cryptoParams">context specific parameters.</param>
+ /// <param name="encryptionAlgorithm">the encryption algorithm to be employed by the cipher.</param>
+ /// <param name="macAlgorithm">the MAC algorithm to be employed by the cipher.</param>
+ /// <returns>a <see cref="TlsCipher"/> implementing the encryption and MAC algorithms.</returns>
+ /// <exception cref="IOException"/>
+ TlsCipher CreateCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm, int macAlgorithm);
+
+ /// <summary>Create a domain object supporting the domain parameters described in dhConfig.</summary>
+ /// <param name="dhConfig">the config describing the DH parameters to use.</param>
+ /// <returns>a TlsDHDomain supporting the parameters in dhConfig.</returns>
+ TlsDHDomain CreateDHDomain(TlsDHConfig dhConfig);
+
+ /// <summary>Create a domain object supporting the domain parameters described in ecConfig.</summary>
+ /// <param name="ecConfig">the config describing the EC parameters to use.</param>
+ /// <returns>a TlsECDomain supporting the parameters in ecConfig.</returns>
+ TlsECDomain CreateECDomain(TlsECConfig ecConfig);
+
+ /// <summary>Adopt the passed in secret, creating a new copy of it.</summary>
+ /// <param name="secret">the secret to make a copy of.</param>
+ /// <returns>a TlsSecret based on the original secret.</returns>
+ TlsSecret AdoptSecret(TlsSecret secret);
+
+ /// <summary>Create a suitable hash for the hash algorithm identifier passed in.</summary>
+ /// <remarks>
+ /// See enumeration class <see cref="CryptoHashAlgorithm"/> for appropriate argument values.
+ /// </remarks>
+ /// <param name="cryptoHashAlgorithm">the hash algorithm the hash needs to implement.</param>
+ /// <returns>a <see cref="TlsHash"/>.</returns>
+ TlsHash CreateHash(int cryptoHashAlgorithm);
+
+ /// <summary>Create a suitable HMAC for the MAC algorithm identifier passed in.</summary>
+ /// <remarks>
+ /// See enumeration class <see cref="MacAlgorithm"/> for appropriate argument values.
+ /// </remarks>
+ /// <param name="macAlgorithm">the MAC algorithm the HMAC needs to match.</param>
+ /// <returns>a <see cref="TlsHmac"/>.</returns>
+ TlsHmac CreateHmac(int macAlgorithm);
+
+ /// <summary>Create a suitable HMAC using the hash algorithm identifier passed in.</summary>
+ /// <remarks>
+ /// See enumeration class <see cref="CryptoHashAlgorithm"/> for appropriate argument values.
+ /// </remarks>
+ /// <param name="cryptoHashAlgorithm">the hash algorithm the HMAC should use.</param>
+ /// <returns>a <see cref="TlsHmac"/>.</returns>
+ TlsHmac CreateHmacForHash(int cryptoHashAlgorithm);
+
+ /// <summary>Create a nonce generator.</summary>
+ /// <remarks>
+ /// Each call should construct a new generator, and the generator should be returned from this call only after
+ /// automatically seeding from this <see cref="TlsCrypto"/>'s entropy source, and from the provided additional
+ /// seed material. The output of each returned generator must be completely independent of the others.
+ /// </remarks>
+ /// <param name="additionalSeedMaterial">context-specific seed material</param>
+ /// <returns>a <see cref="TlsNonceGenerator"/>.</returns>
+ TlsNonceGenerator CreateNonceGenerator(byte[] additionalSeedMaterial);
+
+ /// <summary>Create an SRP-6 client.</summary>
+ /// <param name="srpConfig">client config.</param>
+ /// <returns>an initialised SRP6 client object.</returns>
+ TlsSrp6Client CreateSrp6Client(TlsSrpConfig srpConfig);
+
+ /// <summary>Create an SRP-6 server.</summary>
+ /// <param name="srpConfig">server config.</param>
+ /// <param name="srpVerifier">the SRP6 verifier value.</param>
+ /// <returns>an initialised SRP6 server object.</returns>
+ TlsSrp6Server CreateSrp6Server(TlsSrpConfig srpConfig, BigInteger srpVerifier);
+
+ /// <summary>Create an SRP-6 verifier generator.</summary>
+ /// <param name="srpConfig">generator config.</param>
+ /// <returns>an initialized SRP6 verifier generator.</returns>
+ TlsSrp6VerifierGenerator CreateSrp6VerifierGenerator(TlsSrpConfig srpConfig);
+
+ /// <summary>Setup an initial "secret" for a chain of HKDF calls (RFC 5869), containing a string of HashLen
+ /// zeroes.</summary>
+ /// <param name="cryptoHashAlgorithm">the hash algorithm to instantiate HMAC with. See
+ /// <see cref="CryptoHashAlgorithm"/> for values.</param>
+ TlsSecret HkdfInit(int cryptoHashAlgorithm);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCryptoException.cs b/crypto/src/tls/crypto/TlsCryptoException.cs
new file mode 100644
index 000000000..83c3ef791
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCryptoException.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Basic exception class for crypto services to pass back a cause.</summary>
+ public class TlsCryptoException
+ : TlsException
+ {
+ public TlsCryptoException(string msg)
+ : base(msg)
+ {
+ }
+
+ public TlsCryptoException(string msg, Exception cause)
+ : base(msg, cause)
+ {
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCryptoParameters.cs b/crypto/src/tls/crypto/TlsCryptoParameters.cs
new file mode 100644
index 000000000..008a2dd28
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCryptoParameters.cs
@@ -0,0 +1,49 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ // TODO[tls-port] Would rather this be sealed
+ /// <summary>Carrier class for context-related parameters needed for creating secrets and ciphers.</summary>
+ public class TlsCryptoParameters
+ {
+ private readonly TlsContext m_context;
+
+ /// <summary>Base constructor.</summary>
+ /// <param name="context">the context for this parameters object.</param>
+ public TlsCryptoParameters(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public SecurityParameters SecurityParameters
+ {
+ get { return m_context.SecurityParameters; }
+ }
+
+ public ProtocolVersion ClientVersion
+ {
+ get { return m_context.ClientVersion; }
+ }
+
+ public ProtocolVersion RsaPreMasterSecretVersion
+ {
+ get { return m_context.RsaPreMasterSecretVersion; }
+ }
+
+ // TODO[tls-port] Would rather this be non-virtual
+ public virtual ProtocolVersion ServerVersion
+ {
+ get { return m_context.ServerVersion; }
+ }
+
+ public bool IsServer
+ {
+ get { return m_context.IsServer; }
+ }
+
+ public TlsNonceGenerator NonceGenerator
+ {
+ get { return m_context.NonceGenerator; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsCryptoUtilities.cs b/crypto/src/tls/crypto/TlsCryptoUtilities.cs
new file mode 100644
index 000000000..adea49017
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsCryptoUtilities.cs
@@ -0,0 +1,180 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public abstract class TlsCryptoUtilities
+ {
+ // "tls13 "
+ private static readonly byte[] Tls13Prefix = new byte[] { 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20 };
+
+ public static int GetHash(short hashAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case HashAlgorithm.md5:
+ return CryptoHashAlgorithm.md5;
+ case HashAlgorithm.sha1:
+ return CryptoHashAlgorithm.sha1;
+ case HashAlgorithm.sha224:
+ return CryptoHashAlgorithm.sha224;
+ case HashAlgorithm.sha256:
+ return CryptoHashAlgorithm.sha256;
+ case HashAlgorithm.sha384:
+ return CryptoHashAlgorithm.sha384;
+ case HashAlgorithm.sha512:
+ return CryptoHashAlgorithm.sha512;
+ default:
+ throw new ArgumentException("specified HashAlgorithm invalid: " + HashAlgorithm.GetText(hashAlgorithm));
+ }
+ }
+
+ public static int GetHashForHmac(int macAlgorithm)
+ {
+ switch (macAlgorithm)
+ {
+ case MacAlgorithm.hmac_md5:
+ return CryptoHashAlgorithm.md5;
+ case MacAlgorithm.hmac_sha1:
+ return CryptoHashAlgorithm.sha1;
+ case MacAlgorithm.hmac_sha256:
+ return CryptoHashAlgorithm.sha256;
+ case MacAlgorithm.hmac_sha384:
+ return CryptoHashAlgorithm.sha384;
+ case MacAlgorithm.hmac_sha512:
+ return CryptoHashAlgorithm.sha512;
+ default:
+ throw new ArgumentException("specified MacAlgorithm not an HMAC: " + MacAlgorithm.GetText(macAlgorithm));
+ }
+ }
+
+ public static int GetHashForPrf(int prfAlgorithm)
+ {
+ switch (prfAlgorithm)
+ {
+ case PrfAlgorithm.ssl_prf_legacy:
+ case PrfAlgorithm.tls_prf_legacy:
+ throw new ArgumentException("legacy PRF not a valid algorithm");
+ case PrfAlgorithm.tls_prf_sha256:
+ case PrfAlgorithm.tls13_hkdf_sha256:
+ return CryptoHashAlgorithm.sha256;
+ case PrfAlgorithm.tls_prf_sha384:
+ case PrfAlgorithm.tls13_hkdf_sha384:
+ return CryptoHashAlgorithm.sha384;
+ case PrfAlgorithm.tls13_hkdf_sm3:
+ return CryptoHashAlgorithm.sm3;
+ default:
+ throw new ArgumentException("unknown PrfAlgorithm: " + PrfAlgorithm.GetText(prfAlgorithm));
+ }
+ }
+
+ public static int GetHashOutputSize(int cryptoHashAlgorithm)
+ {
+ switch (cryptoHashAlgorithm)
+ {
+ case CryptoHashAlgorithm.md5:
+ return 16;
+ case CryptoHashAlgorithm.sha1:
+ return 20;
+ case CryptoHashAlgorithm.sha224:
+ return 28;
+ case CryptoHashAlgorithm.sha256:
+ case CryptoHashAlgorithm.sm3:
+ return 32;
+ case CryptoHashAlgorithm.sha384:
+ return 48;
+ case CryptoHashAlgorithm.sha512:
+ return 64;
+ default:
+ throw new ArgumentException();
+ }
+ }
+
+ public static int GetSignature(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ return CryptoSignatureAlgorithm.rsa;
+ case SignatureAlgorithm.dsa:
+ return CryptoSignatureAlgorithm.dsa;
+ case SignatureAlgorithm.ecdsa:
+ return CryptoSignatureAlgorithm.ecdsa;
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ return CryptoSignatureAlgorithm.rsa_pss_rsae_sha256;
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ return CryptoSignatureAlgorithm.rsa_pss_rsae_sha384;
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return CryptoSignatureAlgorithm.rsa_pss_rsae_sha512;
+ case SignatureAlgorithm.ed25519:
+ return CryptoSignatureAlgorithm.ed25519;
+ case SignatureAlgorithm.ed448:
+ return CryptoSignatureAlgorithm.ed448;
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ return CryptoSignatureAlgorithm.rsa_pss_pss_sha256;
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ return CryptoSignatureAlgorithm.rsa_pss_pss_sha384;
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return CryptoSignatureAlgorithm.rsa_pss_pss_sha512;
+ case SignatureAlgorithm.gostr34102012_256:
+ return CryptoSignatureAlgorithm.gostr34102012_256;
+ case SignatureAlgorithm.gostr34102012_512:
+ return CryptoSignatureAlgorithm.gostr34102012_512;
+ default:
+ throw new ArgumentException("specified SignatureAlgorithm invalid: "
+ + SignatureAlgorithm.GetText(signatureAlgorithm));
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public static TlsSecret HkdfExpandLabel(TlsSecret secret, short hashAlgorithm, string label, byte[] context,
+ int length)
+ {
+ int cryptoHashAlgorithm = GetHash(hashAlgorithm);
+
+ return HkdfExpandLabel(secret, cryptoHashAlgorithm, label, context, length);
+ }
+
+ /// <exception cref="IOException"/>
+ public static TlsSecret HkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, string label,
+ byte[] context, int length)
+ {
+ int labelLength = label.Length;
+ if (labelLength < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ int contextLength = context.Length;
+ int expandedLabelLength = Tls13Prefix.Length + labelLength;
+
+ byte[] hkdfLabel = new byte[2 + (1 + expandedLabelLength) + (1 + contextLength)];
+
+ // uint16 length
+ {
+ TlsUtilities.CheckUint16(length);
+ TlsUtilities.WriteUint16(length, hkdfLabel, 0);
+ }
+
+ // opaque label<7..255>
+ {
+ TlsUtilities.CheckUint8(expandedLabelLength);
+ TlsUtilities.WriteUint8(expandedLabelLength, hkdfLabel, 2);
+
+ Array.Copy(Tls13Prefix, 0, hkdfLabel, 2 + 1, Tls13Prefix.Length);
+
+ int labelPos = 2 + (1 + Tls13Prefix.Length);
+ for (int i = 0; i < labelLength; ++i)
+ {
+ char c = label[i];
+ hkdfLabel[labelPos + i] = (byte)c;
+ }
+ }
+
+ // context
+ {
+ TlsUtilities.WriteOpaque8(context, hkdfLabel, 2 + (1 + expandedLabelLength));
+ }
+
+ return secret.HkdfExpand(cryptoHashAlgorithm, hkdfLabel, length);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsDHConfig.cs b/crypto/src/tls/crypto/TlsDHConfig.cs
new file mode 100644
index 000000000..8ec703080
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsDHConfig.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Basic config for Diffie-Hellman.</summary>
+ public class TlsDHConfig
+ {
+ protected readonly DHGroup m_explicitGroup;
+ protected readonly int m_namedGroup;
+ protected readonly bool m_padded;
+
+ public TlsDHConfig(DHGroup explicitGroup)
+ {
+ this.m_explicitGroup = explicitGroup;
+ this.m_namedGroup = -1;
+ this.m_padded = false;
+ }
+
+ public TlsDHConfig(int namedGroup, bool padded)
+ {
+ this.m_explicitGroup = null;
+ this.m_namedGroup = namedGroup;
+ this.m_padded = padded;
+ }
+
+ public virtual DHGroup ExplicitGroup
+ {
+ get { return m_explicitGroup; }
+ }
+
+ public virtual int NamedGroup
+ {
+ get { return m_namedGroup; }
+ }
+
+ public virtual bool IsPadded
+ {
+ get { return m_padded; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsDHDomain.cs b/crypto/src/tls/crypto/TlsDHDomain.cs
new file mode 100644
index 000000000..118441267
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsDHDomain.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Domain interface to service factory for creating Diffie-Hellman operators.</summary>
+ public interface TlsDHDomain
+ {
+ /// <summary>Return an agreement operator suitable for ephemeral Diffie-Hellman.</summary>
+ /// <returns>a key agreement operator.</returns>
+ TlsAgreement CreateDH();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsDecodeResult.cs b/crypto/src/tls/crypto/TlsDecodeResult.cs
new file mode 100644
index 000000000..84b911bcd
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsDecodeResult.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public sealed class TlsDecodeResult
+ {
+ public readonly byte[] buf;
+ public readonly int off, len;
+ public readonly short contentType;
+
+ public TlsDecodeResult(byte[] buf, int off, int len, short contentType)
+ {
+ this.buf = buf;
+ this.off = off;
+ this.len = len;
+ this.contentType = contentType;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsECConfig.cs b/crypto/src/tls/crypto/TlsECConfig.cs
new file mode 100644
index 000000000..156e59df5
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsECConfig.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Carrier class for Elliptic Curve parameter configuration.</summary>
+ public class TlsECConfig
+ {
+ protected readonly int m_namedGroup;
+
+ public TlsECConfig(int namedGroup)
+ {
+ this.m_namedGroup = namedGroup;
+ }
+
+ /// <summary>Return the group used.</summary>
+ /// <returns>the <see cref="NamedGroup">named group</see> used.</returns>
+ public virtual int NamedGroup
+ {
+ get { return m_namedGroup; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsECDomain.cs b/crypto/src/tls/crypto/TlsECDomain.cs
new file mode 100644
index 000000000..74567b38b
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsECDomain.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Domain interface to service factory for creating Elliptic-Curve (EC) based operators.</summary>
+ public interface TlsECDomain
+ {
+ /// <summary>Return an agreement operator suitable for ephemeral EC Diffie-Hellman.</summary>
+ /// <returns>a key agreement operator.</returns>
+ TlsAgreement CreateECDH();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsEncodeResult.cs b/crypto/src/tls/crypto/TlsEncodeResult.cs
new file mode 100644
index 000000000..963e4563a
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsEncodeResult.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public sealed class TlsEncodeResult
+ {
+ public readonly byte[] buf;
+ public readonly int off, len;
+ public readonly short recordType;
+
+ public TlsEncodeResult(byte[] buf, int off, int len, short recordType)
+ {
+ this.buf = buf;
+ this.off = off;
+ this.len = len;
+ this.recordType = recordType;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsHash.cs b/crypto/src/tls/crypto/TlsHash.cs
new file mode 100644
index 000000000..4732fc280
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsHash.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Interface for message digest, or hash, services.</summary>
+ public interface TlsHash
+ {
+ /// <summary>Update the hash with the passed in input.</summary>
+ /// <param name="input">input array containing the data.</param>
+ /// <param name="inOff">offset into the input array the input starts at.</param>
+ /// <param name="length">the length of the input data.</param>
+ void Update(byte[] input, int inOff, int length);
+
+ /// <summary>Return calculated hash for any input passed in.</summary>
+ /// <returns>the hash value.</returns>
+ byte[] CalculateHash();
+
+ /// <summary>Return a clone of this hash object representing its current state.</summary>
+ /// <returns>a clone of the current hash.</returns>
+ TlsHash CloneHash();
+
+ /// <summary>Reset the hash underlying this service.</summary>
+ void Reset();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsHashSink.cs b/crypto/src/tls/crypto/TlsHashSink.cs
new file mode 100644
index 000000000..64496744d
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsHashSink.cs
@@ -0,0 +1,35 @@
+using System;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public class TlsHashSink
+ : BaseOutputStream
+ {
+ private readonly TlsHash m_hash;
+
+ public TlsHashSink(TlsHash hash)
+ {
+ this.m_hash = hash;
+ }
+
+ public virtual TlsHash Hash
+ {
+ get { return m_hash; }
+ }
+
+ public override void WriteByte(byte b)
+ {
+ m_hash.Update(new byte[] { b }, 0, 1);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (len > 0)
+ {
+ m_hash.Update(buf, off, len);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsHmac.cs b/crypto/src/tls/crypto/TlsHmac.cs
new file mode 100644
index 000000000..762954899
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsHmac.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Interface for MAC services based on HMAC.</summary>
+ public interface TlsHmac
+ : TlsMac
+ {
+ /// <summary>Return the internal block size for the message digest underlying this HMAC service.</summary>
+ /// <returns>the internal block size for the digest (in bytes).</returns>
+ int InternalBlockSize { get; }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsMac.cs b/crypto/src/tls/crypto/TlsMac.cs
new file mode 100644
index 000000000..f92d946f1
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsMac.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Interface for MAC services.</summary>
+ public interface TlsMac
+ {
+ /// <summary>Set the key to be used by the MAC implementation supporting this service.</summary>
+ /// <param name="key">array holding the MAC key.</param>
+ /// <param name="keyOff">offset into the array the key starts at.</param>
+ /// <param name="keyLen">length of the key in the array.</param>
+ void SetKey(byte[] key, int keyOff, int keyLen);
+
+ /// <summary>Update the MAC with the passed in input.</summary>
+ /// <param name="input">input array containing the data.</param>
+ /// <param name="inOff">offset into the input array the input starts at.</param>
+ /// <param name="length">the length of the input data.</param>
+ void Update(byte[] input, int inOff, int length);
+
+ /// <summary>Return calculated MAC for any input passed in.</summary>
+ /// <returns>the MAC value.</returns>
+ byte[] CalculateMac();
+
+ /// <summary>Write the calculated MAC to an output buffer.</summary>
+ /// <param name="output">output array to write the MAC to.</param>
+ /// <param name="outOff">offset into the output array to write the MAC to.</param>
+ void CalculateMac(byte[] output, int outOff);
+
+ /// <summary>Return the length of the MAC generated by this service.</summary>
+ /// <returns>the MAC length.</returns>
+ int MacLength { get; }
+
+ /// <summary>Reset the MAC underlying this service.</summary>
+ void Reset();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsMacSink.cs b/crypto/src/tls/crypto/TlsMacSink.cs
new file mode 100644
index 000000000..58e65c731
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsMacSink.cs
@@ -0,0 +1,35 @@
+using System;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public class TlsMacSink
+ : BaseOutputStream
+ {
+ private readonly TlsMac m_mac;
+
+ public TlsMacSink(TlsMac mac)
+ {
+ this.m_mac = mac;
+ }
+
+ public virtual TlsMac Mac
+ {
+ get { return m_mac; }
+ }
+
+ public override void WriteByte(byte b)
+ {
+ m_mac.Update(new byte[]{ b }, 0, 1);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (len > 0)
+ {
+ m_mac.Update(buf, off, len);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsNonceGenerator.cs b/crypto/src/tls/crypto/TlsNonceGenerator.cs
new file mode 100644
index 000000000..00cadc75d
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsNonceGenerator.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public interface TlsNonceGenerator
+ {
+ /// <summary>Generate a nonce byte[] string.</summary>
+ /// <param name="size">the length, in bytes, of the nonce to generate.</param>
+ /// <returns>the nonce value.</returns>
+ byte[] GenerateNonce(int size);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsNullNullCipher.cs b/crypto/src/tls/crypto/TlsNullNullCipher.cs
new file mode 100644
index 000000000..082dff358
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsNullNullCipher.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>The cipher for TLS_NULL_WITH_NULL_NULL.</summary>
+ public sealed class TlsNullNullCipher
+ : TlsCipher
+ {
+ public static readonly TlsNullNullCipher Instance = new TlsNullNullCipher();
+
+ public int GetCiphertextDecodeLimit(int plaintextLimit)
+ {
+ return plaintextLimit;
+ }
+
+ public int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
+ {
+ return plaintextLength;
+ }
+
+ public int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit;
+ }
+
+ public TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+ int headerAllocation, byte[] plaintext, int offset, int len)
+ {
+ byte[] result = new byte[headerAllocation + len];
+ Array.Copy(plaintext, offset, result, headerAllocation, len);
+ return new TlsEncodeResult(result, 0, result.Length, contentType);
+ }
+
+ public TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
+ byte[] ciphertext, int offset, int len)
+ {
+ return new TlsDecodeResult(ciphertext, offset, len, recordType);
+ }
+
+ public void RekeyDecoder()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public void RekeyEncoder()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public bool UsesOpaqueRecordType
+ {
+ get { return false; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsSecret.cs b/crypto/src/tls/crypto/TlsSecret.cs
new file mode 100644
index 000000000..9b092fc40
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsSecret.cs
@@ -0,0 +1,61 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Interface supporting the generation of key material and other SSL/TLS secret values from PRFs.
+ /// </summary>
+ public interface TlsSecret
+ {
+ /// <summary>Return a new secret based on applying a PRF to this one.</summary>
+ /// <param name="prfAlgorithm">PRF algorithm to use.</param>
+ /// <param name="label">the label details.</param>
+ /// <param name="seed">the seed details.</param>
+ /// <param name="length">the size (in bytes) of the secret to generate.</param>
+ /// <returns>the new secret.</returns>
+ TlsSecret DeriveUsingPrf(int prfAlgorithm, string label, byte[] seed, int length);
+
+ /// <summary>Destroy the internal state of the secret.</summary>
+ /// <remarks>
+ /// After this call, any attempt to use the <see cref="TlsSecret"/> will result in an
+ /// <see cref="InvalidOperationException"/> being thrown.
+ /// </remarks>
+ void Destroy();
+
+ /// <summary>Return an encrypted copy of the data this secret is based on.</summary>
+ /// <param name="certificate">the certificate containing the public key to use for protecting the internal
+ /// data.</param>
+ /// <returns>an encrypted copy of this secret's internal data.</returns>
+ /// <exception cref="IOException"/>
+ byte[] Encrypt(TlsCertificate certificate);
+
+ /// <summary>Return the internal data from this secret.</summary>
+ /// <remarks>
+ /// The <see cref="TlsSecret"/> does not keep a copy of the data. After this call, any attempt to use the
+ /// <see cref="TlsSecret"/> will result in an <see cref="InvalidOperationException"/> being thrown.
+ /// </remarks>
+ /// <returns>the secret's internal data.</returns>
+ byte[] Extract();
+
+ /// <summary>RFC 5869 HKDF-Expand function, with this secret's data as the pseudo-random key ('prk').</summary>
+ /// <param name="cryptoHashAlgorithm">the hash algorithm to instantiate HMAC with. See
+ /// <see cref="CryptoHashAlgorithm"/> for values.</param>
+ /// <param name="info">optional context and application specific information (can be zero-length).</param>
+ /// <param name="length">length of output keying material in octets.</param>
+ /// <returns> output keying material (of 'length' octets).</returns>
+ TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length);
+
+ /// <summary>RFC 5869 HKDF-Extract function, with this secret's data as the 'salt'.</summary>
+ /// <remarks>
+ /// The <see cref="TlsSecret"/> does not keep a copy of the data. After this call, any attempt to use
+ /// the <see cref="TlsSecret"/> will result in an <see cref="InvalidOperationException"/> being thrown.
+ /// </remarks>
+ /// <param name="cryptoHashAlgorithm">the hash algorithm to instantiate HMAC with. See
+ /// <see cref="CryptoHashAlgorithm"/> for values.</param>
+ /// <param name="ikm">input keying material.</param>
+ /// <returns>a pseudo-random key (of HashLen octets).</returns>
+ TlsSecret HkdfExtract(int cryptoHashAlgorithm, byte[] ikm);
+
+ bool IsAlive();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsSigner.cs b/crypto/src/tls/crypto/TlsSigner.cs
new file mode 100644
index 000000000..ffd05d935
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsSigner.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Base interface for a TLS signer that works on raw message digests.</summary>
+ public interface TlsSigner
+ {
+ /// <summary>Generate an encoded signature based on the passed in hash.</summary>
+ /// <param name="algorithm">the signature algorithm to use.</param>
+ /// <param name="hash">the hash calculated for the signature.</param>
+ /// <returns>an encoded signature.</returns>
+ /// <exception cref="IOException">in case of an exception processing the hash.</exception>
+ byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash);
+
+ /// <exception cref="IOException"/>
+ TlsStreamSigner GetStreamSigner(SignatureAndHashAlgorithm algorithm);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsSrp6Client.cs b/crypto/src/tls/crypto/TlsSrp6Client.cs
new file mode 100644
index 000000000..e2b545ada
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsSrp6Client.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Basic interface for an SRP-6 client implementation.</summary>
+ public interface TlsSrp6Client
+ {
+ /// <summary>Generates the secret S given the server's credentials</summary>
+ /// <param name="serverB">The server's credentials</param>
+ /// <returns>Client's verification message for the server</returns>
+ /// <exception cref="IOException">If server's credentials are invalid</exception>
+ BigInteger CalculateSecret(BigInteger serverB);
+
+ /// <summary>Generates client's credentials given the client's salt, identity and password</summary>
+ /// <param name="salt">The salt used in the client's verifier.</param>
+ /// <param name="identity">The user's identity (eg. username)</param>
+ /// <param name="password">The user's password</param>
+ /// <returns>Client's public value to send to server</returns>
+ BigInteger GenerateClientCredentials(byte[] salt, byte[] identity, byte[] password);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsSrp6Server.cs b/crypto/src/tls/crypto/TlsSrp6Server.cs
new file mode 100644
index 000000000..75dc6fcb7
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsSrp6Server.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Basic interface for an SRP-6 server implementation.</summary>
+ public interface TlsSrp6Server
+ {
+ /// <summary>Generates the server's credentials that are to be sent to the client.</summary>
+ /// <returns>The server's public value to the client</returns>
+ BigInteger GenerateServerCredentials();
+
+ /// <summary>Processes the client's credentials. If valid the shared secret is generated and returned.
+ /// </summary>
+ /// <param name="clientA">The client's credentials.</param>
+ /// <returns>A shared secret <see cref="BigInteger"/>.</returns>
+ /// <exception cref="IOException">If client's credentials are invalid.</exception>
+ BigInteger CalculateSecret(BigInteger clientA);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsSrp6VerifierGenerator.cs b/crypto/src/tls/crypto/TlsSrp6VerifierGenerator.cs
new file mode 100644
index 000000000..238de98e9
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsSrp6VerifierGenerator.cs
@@ -0,0 +1,17 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Base interface for a generator for SRP-6 verifiers.</summary>
+ public interface TlsSrp6VerifierGenerator
+ {
+ /// <summary>Creates a new SRP-6 verifier value.</summary>
+ /// <param name="salt">The salt to use, generally should be large and random</param>
+ /// <param name="identity">The user's identifying information (eg. username)</param>
+ /// <param name="password">The user's password</param>
+ /// <returns>A new verifier for use in future SRP authentication</returns>
+ BigInteger GenerateVerifier(byte[] salt, byte[] identity, byte[] password);
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsSrpConfig.cs b/crypto/src/tls/crypto/TlsSrpConfig.cs
new file mode 100644
index 000000000..e31e4a5f2
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsSrpConfig.cs
@@ -0,0 +1,26 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Basic config for SRP.</summary>
+ public class TlsSrpConfig
+ {
+ protected BigInteger[] m_explicitNG;
+
+ /// <summary>Return the (N, g) values used in SRP-6.</summary>
+ /// <returns>(N, g) as a BigInteger array (N=[0], g=[1]).</returns>
+ public BigInteger[] GetExplicitNG()
+ {
+ return (BigInteger[])m_explicitNG.Clone();
+ }
+
+ /// <summary>Set the (N, g) values used for SRP-6.</summary>
+ /// <param name="explicitNG">(N, g) as a BigInteger array (N=[0], g=[1]).</param>
+ public void SetExplicitNG(BigInteger[] explicitNG)
+ {
+ this.m_explicitNG = (BigInteger[])explicitNG.Clone();
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsStreamSigner.cs b/crypto/src/tls/crypto/TlsStreamSigner.cs
new file mode 100644
index 000000000..8f79346bf
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsStreamSigner.cs
@@ -0,0 +1,14 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public interface TlsStreamSigner
+ {
+ /// <exception cref="IOException"/>
+ Stream GetOutputStream();
+
+ /// <exception cref="IOException"/>
+ byte[] GetSignature();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsStreamVerifier.cs b/crypto/src/tls/crypto/TlsStreamVerifier.cs
new file mode 100644
index 000000000..9d18a9c9c
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsStreamVerifier.cs
@@ -0,0 +1,14 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ public interface TlsStreamVerifier
+ {
+ /// <exception cref="IOException"/>
+ Stream GetOutputStream();
+
+ /// <exception cref="IOException"/>
+ bool IsVerified();
+ }
+}
diff --git a/crypto/src/tls/crypto/TlsVerifier.cs b/crypto/src/tls/crypto/TlsVerifier.cs
new file mode 100644
index 000000000..ad2ddbc84
--- /dev/null
+++ b/crypto/src/tls/crypto/TlsVerifier.cs
@@ -0,0 +1,20 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto
+{
+ /// <summary>Base interface for a TLS verifier that works with signatures and either raw message digests, or entire
+ /// messages.</summary>
+ public interface TlsVerifier
+ {
+ /// <exception cref="IOException"/>
+ TlsStreamVerifier GetStreamVerifier(DigitallySigned signature);
+
+ /// <summary>Return true if the passed in signature and hash represent a real signature.</summary>
+ /// <param name="signature">the signature object containing the signature to be verified.</param>
+ /// <param name="hash">the hash calculated for the signature.</param>
+ /// <returns>true if signature verifies, false otherwise.</returns>
+ /// <exception cref="IOException">in case of an exception verifying signature.</exception>
+ bool VerifyRawSignature(DigitallySigned signature, byte[] hash);
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs b/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
new file mode 100644
index 000000000..f0b2b03f6
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
@@ -0,0 +1,90 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Base class for a TlsCrypto implementation that provides some needed methods from elsewhere in the impl
+ /// package.</summary>
+ public abstract class AbstractTlsCrypto
+ : TlsCrypto
+ {
+ public abstract bool HasAllRawSignatureAlgorithms();
+
+ public abstract bool HasDHAgreement();
+
+ public abstract bool HasECDHAgreement();
+
+ public abstract bool HasEncryptionAlgorithm(int encryptionAlgorithm);
+
+ public abstract bool HasCryptoHashAlgorithm(int cryptoHashAlgorithm);
+
+ public abstract bool HasCryptoSignatureAlgorithm(int cryptoSignatureAlgorithm);
+
+ public abstract bool HasMacAlgorithm(int macAlgorithm);
+
+ public abstract bool HasNamedGroup(int namedGroup);
+
+ public abstract bool HasRsaEncryption();
+
+ public abstract bool HasSignatureAlgorithm(short signatureAlgorithm);
+
+ public abstract bool HasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm);
+
+ public abstract bool HasSignatureScheme(int signatureScheme);
+
+ public abstract bool HasSrpAuthentication();
+
+ public abstract TlsSecret CreateSecret(byte[] data);
+
+ public abstract TlsSecret GenerateRsaPreMasterSecret(ProtocolVersion clientVersion);
+
+ public abstract SecureRandom SecureRandom { get; }
+
+ public abstract TlsCertificate CreateCertificate(byte[] encoding);
+
+ public abstract TlsCipher CreateCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm, int macAlgorithm);
+
+ public abstract TlsDHDomain CreateDHDomain(TlsDHConfig dhConfig);
+
+ public abstract TlsECDomain CreateECDomain(TlsECConfig ecConfig);
+
+ public virtual TlsSecret AdoptSecret(TlsSecret secret)
+ {
+ // TODO[tls] Need an alternative that doesn't require AbstractTlsSecret (which holds literal data)
+ if (secret is AbstractTlsSecret)
+ {
+ AbstractTlsSecret sec = (AbstractTlsSecret)secret;
+
+ return CreateSecret(sec.CopyData());
+ }
+
+ throw new ArgumentException("unrecognized TlsSecret - cannot copy data: " + Platform.GetTypeName(secret));
+ }
+
+ public abstract TlsHash CreateHash(int cryptoHashAlgorithm);
+
+ public abstract TlsHmac CreateHmac(int macAlgorithm);
+
+ public abstract TlsHmac CreateHmacForHash(int cryptoHashAlgorithm);
+
+ public abstract TlsNonceGenerator CreateNonceGenerator(byte[] additionalSeedMaterial);
+
+ public abstract TlsSrp6Client CreateSrp6Client(TlsSrpConfig srpConfig);
+
+ public abstract TlsSrp6Server CreateSrp6Server(TlsSrpConfig srpConfig, BigInteger srpVerifier);
+
+ public abstract TlsSrp6VerifierGenerator CreateSrp6VerifierGenerator(TlsSrpConfig srpConfig);
+
+ public abstract TlsSecret HkdfInit(int cryptoHashAlgorithm);
+
+ /// <summary>Return an encryptor based on the public key in certificate.</summary>
+ /// <param name="certificate">the certificate carrying the public key.</param>
+ /// <returns>a <see cref="TlsEncryptor"/> based on the certificate's public key.</returns>
+ /// <exception cref="IOException"/>
+ public abstract TlsEncryptor CreateEncryptor(TlsCertificate certificate);
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs b/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs
new file mode 100644
index 000000000..634b86732
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs
@@ -0,0 +1,87 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Base class for a TlsSecret implementation which captures common code and fields.</summary>
+ public abstract class AbstractTlsSecret
+ : TlsSecret
+ {
+ protected byte[] m_data;
+
+ /// <summary>Base constructor.</summary>
+ /// <param name="data">the byte[] making up the secret value.</param>
+ protected AbstractTlsSecret(byte[] data)
+ {
+ this.m_data = data;
+ }
+
+ protected virtual void CheckAlive()
+ {
+ if (m_data == null)
+ throw new InvalidOperationException("Secret has already been extracted or destroyed");
+ }
+
+ protected abstract AbstractTlsCrypto Crypto { get; }
+
+ public abstract TlsSecret DeriveUsingPrf(int prfAlgorithm, string label, byte[] seed, int length);
+
+ public virtual void Destroy()
+ {
+ lock (this)
+ {
+ if (m_data != null)
+ {
+ // TODO Is there a way to ensure the data is really overwritten?
+ Array.Clear(m_data, 0, m_data.Length);
+ this.m_data = null;
+ }
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual byte[] Encrypt(TlsCertificate certificate)
+ {
+ lock (this)
+ {
+ CheckAlive();
+
+ return Crypto.CreateEncryptor(certificate).Encrypt(m_data, 0, m_data.Length);
+ }
+ }
+
+ public virtual byte[] Extract()
+ {
+ lock (this)
+ {
+ CheckAlive();
+
+ byte[] result = m_data;
+ this.m_data = null;
+ return result;
+ }
+ }
+
+ public abstract TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length);
+
+ public abstract TlsSecret HkdfExtract(int cryptoHashAlgorithm, byte[] ikm);
+
+ public virtual bool IsAlive()
+ {
+ lock (this)
+ {
+ return null != m_data;
+ }
+ }
+
+ internal virtual byte[] CopyData()
+ {
+ lock (this)
+ {
+ return Arrays.Clone(m_data);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/RsaUtilities.cs b/crypto/src/tls/crypto/impl/RsaUtilities.cs
new file mode 100644
index 000000000..83782a2ac
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/RsaUtilities.cs
@@ -0,0 +1,136 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ public abstract class RsaUtilities
+ {
+ private static readonly byte[] RSAPSSParams_256_A, RSAPSSParams_384_A, RSAPSSParams_512_A;
+ private static readonly byte[] RSAPSSParams_256_B, RSAPSSParams_384_B, RSAPSSParams_512_B;
+
+ static RsaUtilities()
+ {
+ /*
+ * RFC 4055
+ */
+
+ AlgorithmIdentifier sha256Identifier_A = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256);
+ AlgorithmIdentifier sha384Identifier_A = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384);
+ AlgorithmIdentifier sha512Identifier_A = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512);
+ AlgorithmIdentifier sha256Identifier_B = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance);
+ AlgorithmIdentifier sha384Identifier_B = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance);
+ AlgorithmIdentifier sha512Identifier_B = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance);
+
+ AlgorithmIdentifier mgf1SHA256Identifier_A = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, sha256Identifier_A);
+ AlgorithmIdentifier mgf1SHA384Identifier_A = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, sha384Identifier_A);
+ AlgorithmIdentifier mgf1SHA512Identifier_A = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, sha512Identifier_A);
+ AlgorithmIdentifier mgf1SHA256Identifier_B = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, sha256Identifier_B);
+ AlgorithmIdentifier mgf1SHA384Identifier_B = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, sha384Identifier_B);
+ AlgorithmIdentifier mgf1SHA512Identifier_B = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, sha512Identifier_B);
+
+ DerInteger sha256Size = new DerInteger(TlsCryptoUtilities.GetHashOutputSize(CryptoHashAlgorithm.sha256));
+ DerInteger sha384Size = new DerInteger(TlsCryptoUtilities.GetHashOutputSize(CryptoHashAlgorithm.sha384));
+ DerInteger sha512Size = new DerInteger(TlsCryptoUtilities.GetHashOutputSize(CryptoHashAlgorithm.sha512));
+
+ DerInteger trailerField = new DerInteger(1);
+
+ try
+ {
+ RSAPSSParams_256_A = new RsassaPssParameters(sha256Identifier_A, mgf1SHA256Identifier_A, sha256Size, trailerField)
+ .GetEncoded(Asn1Encodable.Der);
+ RSAPSSParams_384_A = new RsassaPssParameters(sha384Identifier_A, mgf1SHA384Identifier_A, sha384Size, trailerField)
+ .GetEncoded(Asn1Encodable.Der);
+ RSAPSSParams_512_A = new RsassaPssParameters(sha512Identifier_A, mgf1SHA512Identifier_A, sha512Size, trailerField)
+ .GetEncoded(Asn1Encodable.Der);
+ RSAPSSParams_256_B = new RsassaPssParameters(sha256Identifier_B, mgf1SHA256Identifier_B, sha256Size, trailerField)
+ .GetEncoded(Asn1Encodable.Der);
+ RSAPSSParams_384_B = new RsassaPssParameters(sha384Identifier_B, mgf1SHA384Identifier_B, sha384Size, trailerField)
+ .GetEncoded(Asn1Encodable.Der);
+ RSAPSSParams_512_B = new RsassaPssParameters(sha512Identifier_B, mgf1SHA512Identifier_B, sha512Size, trailerField)
+ .GetEncoded(Asn1Encodable.Der);
+ }
+ catch (IOException e)
+ {
+ throw new InvalidOperationException(e.Message);
+ }
+ }
+
+ public static bool SupportsPkcs1(AlgorithmIdentifier pubKeyAlgID)
+ {
+ DerObjectIdentifier oid = pubKeyAlgID.Algorithm;
+ return PkcsObjectIdentifiers.RsaEncryption.Equals(oid)
+ || X509ObjectIdentifiers.IdEARsa.Equals(oid);
+ }
+
+ public static bool SupportsPss_Pss(short signatureAlgorithm, AlgorithmIdentifier pubKeyAlgID)
+ {
+ DerObjectIdentifier oid = pubKeyAlgID.Algorithm;
+ if (!PkcsObjectIdentifiers.IdRsassaPss.Equals(oid))
+ return false;
+
+ /*
+ * TODO ASN.1 NULL shouldn't really be allowed here; it's a workaround for e.g. Oracle JDK
+ * 1.8.0_241, where the X.509 certificate implementation adds the NULL when re-encoding the
+ * original parameters. It appears it was fixed at some later date (OpenJDK 12.0.2 does not
+ * have the issue), but not sure exactly when.
+ */
+ Asn1Encodable pssParams = pubKeyAlgID.Parameters;
+ if (null == pssParams || pssParams is DerNull)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ byte[] encoded;
+ try
+ {
+ encoded = pssParams.ToAsn1Object().GetEncoded(Asn1Encodable.Der);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ byte[] expected_A, expected_B;
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ expected_A = RSAPSSParams_256_A;
+ expected_B = RSAPSSParams_256_B;
+ break;
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ expected_A = RSAPSSParams_384_A;
+ expected_B = RSAPSSParams_384_B;
+ break;
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ expected_A = RSAPSSParams_512_A;
+ expected_B = RSAPSSParams_512_B;
+ break;
+ default:
+ return false;
+ }
+
+ return Arrays.AreEqual(expected_A, encoded)
+ || Arrays.AreEqual(expected_B, encoded);
+ }
+
+ public static bool SupportsPss_Rsae(AlgorithmIdentifier pubKeyAlgID)
+ {
+ DerObjectIdentifier oid = pubKeyAlgID.Algorithm;
+ return PkcsObjectIdentifiers.RsaEncryption.Equals(oid);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
new file mode 100644
index 000000000..80851e440
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
@@ -0,0 +1,377 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>A generic TLS 1.2 AEAD cipher.</summary>
+ public class TlsAeadCipher
+ : TlsCipher
+ {
+ public const int AEAD_CCM = 1;
+ public const int AEAD_CHACHA20_POLY1305 = 2;
+ public const int AEAD_GCM = 3;
+
+ private const int NONCE_RFC5288 = 1;
+ private const int NONCE_RFC7905 = 2;
+
+ protected readonly TlsCryptoParameters m_cryptoParams;
+ protected readonly int m_keySize;
+ protected readonly int m_macSize;
+ protected readonly int m_fixed_iv_length;
+ protected readonly int m_record_iv_length;
+
+ protected readonly TlsAeadCipherImpl m_decryptCipher, m_encryptCipher;
+ protected readonly byte[] m_decryptNonce, m_encryptNonce;
+
+ protected readonly bool m_isTlsV13;
+ protected readonly int m_nonceMode;
+
+ /// <exception cref="IOException"/>
+ public TlsAeadCipher(TlsCryptoParameters cryptoParams, TlsAeadCipherImpl encryptCipher,
+ TlsAeadCipherImpl decryptCipher, int keySize, int macSize, int aeadType)
+ {
+ SecurityParameters securityParameters = cryptoParams.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (!TlsImplUtilities.IsTlsV12(negotiatedVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_isTlsV13 = TlsImplUtilities.IsTlsV13(negotiatedVersion);
+ this.m_nonceMode = GetNonceMode(m_isTlsV13, aeadType);
+
+ switch (m_nonceMode)
+ {
+ case NONCE_RFC5288:
+ this.m_fixed_iv_length = 4;
+ this.m_record_iv_length = 8;
+ break;
+ case NONCE_RFC7905:
+ this.m_fixed_iv_length = 12;
+ this.m_record_iv_length = 0;
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.m_cryptoParams = cryptoParams;
+ this.m_keySize = keySize;
+ this.m_macSize = macSize;
+
+ this.m_decryptCipher = decryptCipher;
+ this.m_encryptCipher = encryptCipher;
+
+ this.m_decryptNonce = new byte[m_fixed_iv_length];
+ this.m_encryptNonce = new byte[m_fixed_iv_length];
+
+ bool isServer = cryptoParams.IsServer;
+ if (m_isTlsV13)
+ {
+ RekeyCipher(securityParameters, decryptCipher, m_decryptNonce, !isServer);
+ RekeyCipher(securityParameters, encryptCipher, m_encryptNonce, isServer);
+ return;
+ }
+
+ int keyBlockSize = (2 * keySize) + (2 * m_fixed_iv_length);
+ byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
+ int pos = 0;
+
+ if (isServer)
+ {
+ decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
+ encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
+
+ Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
+ Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
+ }
+ else
+ {
+ encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
+ decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
+
+ Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
+ Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
+ }
+
+ if (keyBlockSize != pos)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ int nonceLength = m_fixed_iv_length + m_record_iv_length;
+
+ // NOTE: Ensure dummy nonce is not part of the generated sequence(s)
+ byte[] dummyNonce = new byte[nonceLength];
+ dummyNonce[0] = (byte)~m_encryptNonce[0];
+ dummyNonce[1] = (byte)~m_decryptNonce[1];
+
+ encryptCipher.Init(dummyNonce, macSize, null);
+ decryptCipher.Init(dummyNonce, macSize, null);
+ }
+
+ public virtual int GetCiphertextDecodeLimit(int plaintextLimit)
+ {
+ return plaintextLimit + m_macSize + m_record_iv_length + (m_isTlsV13 ? 1 : 0);
+ }
+
+ public virtual int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
+ {
+ int innerPlaintextLimit = plaintextLength;
+ if (m_isTlsV13)
+ {
+ // TODO[tls13] Add support for padding
+ int maxPadding = 0;
+
+ innerPlaintextLimit = 1 + System.Math.Min(plaintextLimit, plaintextLength + maxPadding);
+ }
+
+ return innerPlaintextLimit + m_macSize + m_record_iv_length;
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - m_macSize - m_record_iv_length - (m_isTlsV13 ? 1 : 0);
+ }
+
+ public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+ int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength)
+ {
+ byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
+
+ switch (m_nonceMode)
+ {
+ case NONCE_RFC5288:
+ Array.Copy(m_encryptNonce, 0, nonce, 0, m_encryptNonce.Length);
+ // RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
+ TlsUtilities.WriteUint64(seqNo, nonce, m_encryptNonce.Length);
+ break;
+ case NONCE_RFC7905:
+ TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
+ for (int i = 0; i < m_encryptNonce.Length; ++i)
+ {
+ nonce[i] ^= m_encryptNonce[i];
+ }
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ int extraLength = m_isTlsV13 ? 1 : 0;
+
+ // TODO[tls13] If we support adding padding to TLSInnerPlaintext, this will need review
+ int encryptionLength = m_encryptCipher.GetOutputSize(plaintextLength + extraLength);
+ int ciphertextLength = m_record_iv_length + encryptionLength;
+
+ byte[] output = new byte[headerAllocation + ciphertextLength];
+ int outputPos = headerAllocation;
+
+ if (m_record_iv_length != 0)
+ {
+ Array.Copy(nonce, nonce.Length - m_record_iv_length, output, outputPos, m_record_iv_length);
+ outputPos += m_record_iv_length;
+ }
+
+ short recordType = m_isTlsV13 ? ContentType.application_data : contentType;
+
+ byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
+ plaintextLength);
+
+ try
+ {
+ m_encryptCipher.Init(nonce, m_macSize, additionalData);
+
+ Array.Copy(plaintext, plaintextOffset, output, outputPos, plaintextLength);
+ if (m_isTlsV13)
+ {
+ output[outputPos + plaintextLength] = (byte)contentType;
+ }
+
+ outputPos += m_encryptCipher.DoFinal(output, outputPos, plaintextLength + extraLength, output,
+ outputPos);
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+
+ if (outputPos != output.Length)
+ {
+ // NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return new TlsEncodeResult(output, 0, output.Length, recordType);
+ }
+
+ public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
+ byte[] ciphertext, int ciphertextOffset, int ciphertextLength)
+ {
+ if (GetPlaintextLimit(ciphertextLength) < 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] nonce = new byte[m_decryptNonce.Length + m_record_iv_length];
+
+ switch (m_nonceMode)
+ {
+ case NONCE_RFC5288:
+ Array.Copy(m_decryptNonce, 0, nonce, 0, m_decryptNonce.Length);
+ Array.Copy(ciphertext, ciphertextOffset, nonce, nonce.Length - m_record_iv_length,
+ m_record_iv_length);
+ break;
+ case NONCE_RFC7905:
+ TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
+ for (int i = 0; i < m_decryptNonce.Length; ++i)
+ {
+ nonce[i] ^= m_decryptNonce[i];
+ }
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ int encryptionOffset = ciphertextOffset + m_record_iv_length;
+ int encryptionLength = ciphertextLength - m_record_iv_length;
+ int plaintextLength = m_decryptCipher.GetOutputSize(encryptionLength);
+
+ byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
+ plaintextLength);
+
+ int outputPos;
+ try
+ {
+ m_decryptCipher.Init(nonce, m_macSize, additionalData);
+ outputPos = m_decryptCipher.DoFinal(ciphertext, encryptionOffset, encryptionLength, ciphertext,
+ encryptionOffset);
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
+ }
+
+ if (outputPos != plaintextLength)
+ {
+ // NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ short contentType = recordType;
+ if (m_isTlsV13)
+ {
+ // Strip padding and read true content type from TLSInnerPlaintext
+ int pos = plaintextLength;
+ for (;;)
+ {
+ if (--pos < 0)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ byte octet = ciphertext[encryptionOffset + pos];
+ if (0 != octet)
+ {
+ contentType = (short)(octet & 0xFF);
+ plaintextLength = pos;
+ break;
+ }
+ }
+ }
+
+ return new TlsDecodeResult(ciphertext, encryptionOffset, plaintextLength, contentType);
+ }
+
+ public virtual void RekeyDecoder()
+ {
+ RekeyCipher(m_cryptoParams.SecurityParameters, m_decryptCipher, m_decryptNonce, !m_cryptoParams.IsServer);
+ }
+
+ public virtual void RekeyEncoder()
+ {
+ RekeyCipher(m_cryptoParams.SecurityParameters, m_encryptCipher, m_encryptNonce, m_cryptoParams.IsServer);
+ }
+
+ public virtual bool UsesOpaqueRecordType
+ {
+ get { return m_isTlsV13; }
+ }
+
+ protected virtual byte[] GetAdditionalData(long seqNo, short recordType, ProtocolVersion recordVersion,
+ int ciphertextLength, int plaintextLength)
+ {
+ if (m_isTlsV13)
+ {
+ /*
+ * TLSCiphertext.opaque_type || TLSCiphertext.legacy_record_version || TLSCiphertext.length
+ */
+ byte[] additional_data = new byte[5];
+ TlsUtilities.WriteUint8(recordType, additional_data, 0);
+ TlsUtilities.WriteVersion(recordVersion, additional_data, 1);
+ TlsUtilities.WriteUint16(ciphertextLength, additional_data, 3);
+ return additional_data;
+ }
+ else
+ {
+ /*
+ * seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length
+ */
+ byte[] additional_data = new byte[13];
+ TlsUtilities.WriteUint64(seqNo, additional_data, 0);
+ TlsUtilities.WriteUint8(recordType, additional_data, 8);
+ TlsUtilities.WriteVersion(recordVersion, additional_data, 9);
+ TlsUtilities.WriteUint16(plaintextLength, additional_data, 11);
+ return additional_data;
+ }
+ }
+
+ protected virtual void RekeyCipher(SecurityParameters securityParameters, TlsAeadCipherImpl cipher,
+ byte[] nonce, bool serverSecret)
+ {
+ if (!m_isTlsV13)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ TlsSecret secret = serverSecret
+ ? securityParameters.TrafficSecretServer
+ : securityParameters.TrafficSecretClient;
+
+ // TODO[tls13] For early data, have to disable server->client
+ if (null == secret)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ Setup13Cipher(cipher, nonce, secret, TlsCryptoUtilities.GetHash(securityParameters.PrfHashAlgorithm));
+ }
+
+ protected virtual void Setup13Cipher(TlsAeadCipherImpl cipher, byte[] nonce, TlsSecret secret,
+ int cryptoHashAlgorithm)
+ {
+ byte[] key = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "key",
+ TlsUtilities.EmptyBytes, m_keySize).Extract();
+ byte[] iv = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", TlsUtilities.EmptyBytes,
+ m_fixed_iv_length).Extract();
+
+ cipher.SetKey(key, 0, m_keySize);
+ Array.Copy(iv, 0, nonce, 0, m_fixed_iv_length);
+
+ // NOTE: Ensure dummy nonce is not part of the generated sequence(s)
+ iv [0] ^= 0x80;
+ cipher.Init(iv, m_macSize, null);
+ }
+
+ private static int GetNonceMode(bool isTLSv13, int aeadType)
+ {
+ switch (aeadType)
+ {
+ case AEAD_CCM:
+ case AEAD_GCM:
+ return isTLSv13 ? NONCE_RFC7905 : NONCE_RFC5288;
+
+ case AEAD_CHACHA20_POLY1305:
+ return NONCE_RFC7905;
+
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
new file mode 100644
index 000000000..44e6fda84
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
@@ -0,0 +1,41 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Base interface for services supporting AEAD encryption/decryption.</summary>
+ public interface TlsAeadCipherImpl
+ {
+ /// <summary>Set the key to be used by the AEAD cipher implementation supporting this service.</summary>
+ /// <param name="key">array holding the AEAD cipher key.</param>
+ /// <param name="keyOff">offset into the array the key starts at.</param>
+ /// <param name="keyLen">length of the key in the array.</param>
+ /// <exception cref="IOException"/>
+ void SetKey(byte[] key, int keyOff, int keyLen);
+
+ /// <summary>Initialise the parameters for the AEAD operator.</summary>
+ /// <param name="nonce">the nonce.</param>
+ /// <param name="macSize">MAC size in bytes.</param>
+ /// <param name="additionalData">any additional data to be included in the MAC calculation.</param>
+ /// <exception cref="IOException">if the parameters are inappropriate.</exception>
+ void Init(byte[] nonce, int macSize, byte[] additionalData);
+
+ /// <summary>Return the maximum size of the output for input of inputLength bytes.</summary>
+ /// <param name="inputLength">the length (in bytes) of the proposed input.</param>
+ /// <returns>the maximum size of the output.</returns>
+ int GetOutputSize(int inputLength);
+
+ /// <summary>Perform the cipher encryption/decryption returning the output in output.</summary>
+ /// <remarks>
+ /// Note: we have to use DoFinal() here as it is the only way to guarantee output from the underlying cipher.
+ /// </remarks>
+ /// <param name="input">array holding input data to the cipher.</param>
+ /// <param name="inputOffset">offset into input array data starts at.</param>
+ /// <param name="inputLength">length of the input data in the array.</param>
+ /// <param name="output">array to hold the cipher output.</param>
+ /// <param name="outputOffset">offset into output array to start saving output.</param>
+ /// <returns>the amount of data written to output.</returns>
+ /// <exception cref="IOException">in case of failure.</exception>
+ int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset);
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
new file mode 100644
index 000000000..64cfc752a
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
@@ -0,0 +1,423 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>A generic TLS 1.0-1.2 block cipher. This can be used for AES or 3DES for example.</summary>
+ public class TlsBlockCipher
+ : TlsCipher
+ {
+ protected readonly TlsCryptoParameters m_cryptoParams;
+ protected readonly byte[] m_randomData;
+ protected readonly bool m_encryptThenMac;
+ protected readonly bool m_useExplicitIV;
+ protected readonly bool m_acceptExtraPadding;
+ protected readonly bool m_useExtraPadding;
+
+ protected readonly TlsBlockCipherImpl m_decryptCipher, m_encryptCipher;
+ protected readonly TlsSuiteMac m_readMac, m_writeMac;
+
+ /// <exception cref="IOException"/>
+ public TlsBlockCipher(TlsCryptoParameters cryptoParams, TlsBlockCipherImpl encryptCipher,
+ TlsBlockCipherImpl decryptCipher, TlsHmac clientMac, TlsHmac serverMac, int cipherKeySize)
+ {
+ SecurityParameters securityParameters = cryptoParams.SecurityParameters;
+ ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
+
+ if (TlsImplUtilities.IsTlsV13(negotiatedVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_cryptoParams = cryptoParams;
+ this.m_randomData = cryptoParams.NonceGenerator.GenerateNonce(256);
+
+ this.m_encryptThenMac = securityParameters.IsEncryptThenMac;
+ this.m_useExplicitIV = TlsImplUtilities.IsTlsV11(negotiatedVersion);
+
+ this.m_acceptExtraPadding = !negotiatedVersion.IsSsl;
+
+ /*
+ * Don't use variable-length padding with truncated MACs.
+ *
+ * See "Tag Size Does Matter: Attacks and Proofs for the TLS Record Protocol", Paterson,
+ * Ristenpart, Shrimpton.
+ *
+ * TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
+ */
+ this.m_useExtraPadding = securityParameters.IsExtendedPadding
+ && ProtocolVersion.TLSv10.IsEqualOrEarlierVersionOf(negotiatedVersion)
+ && (m_encryptThenMac || !securityParameters.IsTruncatedHmac);
+
+ this.m_encryptCipher = encryptCipher;
+ this.m_decryptCipher = decryptCipher;
+
+ TlsBlockCipherImpl clientCipher, serverCipher;
+ if (cryptoParams.IsServer)
+ {
+ clientCipher = decryptCipher;
+ serverCipher = encryptCipher;
+ }
+ else
+ {
+ clientCipher = encryptCipher;
+ serverCipher = decryptCipher;
+ }
+
+ int key_block_size = (2 * cipherKeySize) + clientMac.MacLength + serverMac.MacLength;
+
+ // From TLS 1.1 onwards, block ciphers don't need IVs from the key_block
+ if (!m_useExplicitIV)
+ {
+ key_block_size += clientCipher.GetBlockSize() + serverCipher.GetBlockSize();
+ }
+
+ byte[] key_block = TlsImplUtilities.CalculateKeyBlock(cryptoParams, key_block_size);
+
+ int offset = 0;
+
+ clientMac.SetKey(key_block, offset, clientMac.MacLength);
+ offset += clientMac.MacLength;
+ serverMac.SetKey(key_block, offset, serverMac.MacLength);
+ offset += serverMac.MacLength;
+
+ clientCipher.SetKey(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ serverCipher.SetKey(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+
+ int clientIVLength = clientCipher.GetBlockSize();
+ int serverIVLength = serverCipher.GetBlockSize();
+
+ if (m_useExplicitIV)
+ {
+ clientCipher.Init(new byte[clientIVLength], 0, clientIVLength);
+ serverCipher.Init(new byte[serverIVLength], 0, serverIVLength);
+ }
+ else
+ {
+ clientCipher.Init(key_block, offset, clientIVLength);
+ offset += clientIVLength;
+ serverCipher.Init(key_block, offset, serverIVLength);
+ offset += serverIVLength;
+ }
+
+ if (offset != key_block_size)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (cryptoParams.IsServer)
+ {
+ this.m_writeMac = new TlsSuiteHmac(cryptoParams, serverMac);
+ this.m_readMac = new TlsSuiteHmac(cryptoParams, clientMac);
+ }
+ else
+ {
+ this.m_writeMac = new TlsSuiteHmac(cryptoParams, clientMac);
+ this.m_readMac = new TlsSuiteHmac(cryptoParams, serverMac);
+ }
+ }
+
+ public virtual int GetCiphertextDecodeLimit(int plaintextLimit)
+ {
+ int blockSize = m_decryptCipher.GetBlockSize();
+ int macSize = m_readMac.Size;
+ int maxPadding = 256;
+
+ return GetCiphertextLength(blockSize, macSize, maxPadding, plaintextLimit);
+ }
+
+ public virtual int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
+ {
+ int blockSize = m_encryptCipher.GetBlockSize();
+ int macSize = m_writeMac.Size;
+ int maxPadding = m_useExtraPadding ? 256 : blockSize;
+
+ return GetCiphertextLength(blockSize, macSize, maxPadding, plaintextLength);
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ int blockSize = m_encryptCipher.GetBlockSize();
+ int macSize = m_writeMac.Size;
+
+ int plaintextLimit = ciphertextLimit;
+
+ // Leave room for the MAC, and require block-alignment
+ if (m_encryptThenMac)
+ {
+ plaintextLimit -= macSize;
+ plaintextLimit -= plaintextLimit % blockSize;
+ }
+ else
+ {
+ plaintextLimit -= plaintextLimit % blockSize;
+ plaintextLimit -= macSize;
+ }
+
+ // Minimum 1 byte of padding
+ --plaintextLimit;
+
+ // An explicit IV consumes 1 block
+ if (m_useExplicitIV)
+ {
+ plaintextLimit -= blockSize;
+ }
+
+ return plaintextLimit;
+ }
+
+ public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+ int headerAllocation, byte[] plaintext, int offset, int len)
+ {
+ int blockSize = m_encryptCipher.GetBlockSize();
+ int macSize = m_writeMac.Size;
+
+ int enc_input_length = len;
+ if (!m_encryptThenMac)
+ {
+ enc_input_length += macSize;
+ }
+
+ int padding_length = blockSize - (enc_input_length % blockSize);
+ if (m_useExtraPadding)
+ {
+ // Add a random number of extra blocks worth of padding
+ int maxExtraPadBlocks = (256 - padding_length) / blockSize;
+ int actualExtraPadBlocks = ChooseExtraPadBlocks(maxExtraPadBlocks);
+ padding_length += actualExtraPadBlocks * blockSize;
+ }
+
+ int totalSize = len + macSize + padding_length;
+ if (m_useExplicitIV)
+ {
+ totalSize += blockSize;
+ }
+
+ byte[] outBuf = new byte[headerAllocation + totalSize];
+ int outOff = headerAllocation;
+
+ if (m_useExplicitIV)
+ {
+ // Technically the explicit IV will be the encryption of this nonce
+ byte[] explicitIV = m_cryptoParams.NonceGenerator.GenerateNonce(blockSize);
+ Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
+ outOff += blockSize;
+ }
+
+ Array.Copy(plaintext, offset, outBuf, outOff, len);
+ outOff += len;
+
+ if (!m_encryptThenMac)
+ {
+ byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext, offset, len);
+ Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+ outOff += mac.Length;
+ }
+
+ byte padByte = (byte)(padding_length - 1);
+ for (int i = 0; i < padding_length; ++i)
+ {
+ outBuf[outOff++] = padByte;
+ }
+
+ m_encryptCipher.DoFinal(outBuf, headerAllocation, outOff - headerAllocation, outBuf, headerAllocation);
+
+ if (m_encryptThenMac)
+ {
+ byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, outBuf, headerAllocation,
+ outOff - headerAllocation);
+ Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+ outOff += mac.Length;
+ }
+
+ if (outOff != outBuf.Length)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return new TlsEncodeResult(outBuf, 0, outBuf.Length, contentType);
+ }
+
+ public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
+ byte[] ciphertext, int offset, int len)
+ {
+ int blockSize = m_decryptCipher.GetBlockSize();
+ int macSize = m_readMac.Size;
+
+ int minLen = blockSize;
+ if (m_encryptThenMac)
+ {
+ minLen += macSize;
+ }
+ else
+ {
+ minLen = System.Math.Max(minLen, macSize + 1);
+ }
+
+ if (m_useExplicitIV)
+ {
+ minLen += blockSize;
+ }
+
+ if (len < minLen)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int blocks_length = len;
+ if (m_encryptThenMac)
+ {
+ blocks_length -= macSize;
+ }
+
+ if (blocks_length % blockSize != 0)
+ throw new TlsFatalAlert(AlertDescription.decryption_failed);
+
+ if (m_encryptThenMac)
+ {
+ byte[] expectedMac = m_readMac.CalculateMac(seqNo, recordType, ciphertext, offset, len - macSize);
+
+ bool checkMac = TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext,
+ offset + len - macSize);
+ if (!checkMac)
+ {
+ /*
+ * 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);
+ }
+ }
+
+ m_decryptCipher.DoFinal(ciphertext, offset, blocks_length, ciphertext, offset);
+
+ if (m_useExplicitIV)
+ {
+ offset += blockSize;
+ blocks_length -= blockSize;
+ }
+
+ // If there's anything wrong with the padding, this will return zero
+ int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize,
+ m_encryptThenMac ? 0 : macSize);
+ bool badMac = (totalPad == 0);
+
+ int dec_output_length = blocks_length - totalPad;
+
+ if (!m_encryptThenMac)
+ {
+ dec_output_length -= macSize;
+
+ byte[] expectedMac = m_readMac.CalculateMacConstantTime(seqNo, recordType, ciphertext, offset,
+ dec_output_length, blocks_length - macSize, m_randomData);
+
+ badMac |= !TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext,
+ offset + dec_output_length);
+ }
+
+ if (badMac)
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ return new TlsDecodeResult(ciphertext, offset, dec_output_length, recordType);
+ }
+
+ public virtual void RekeyDecoder()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual void RekeyEncoder()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual bool UsesOpaqueRecordType
+ {
+ get { return false; }
+ }
+
+ protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
+ {
+ int end = off + len;
+ byte lastByte = buf[end - 1];
+ int padlen = lastByte & 0xff;
+ int totalPad = padlen + 1;
+
+ int dummyIndex = 0;
+ byte padDiff = 0;
+
+ int totalPadLimit = System.Math.Min(m_acceptExtraPadding ? 256 : blockSize, len - macSize);
+
+ if (totalPad > totalPadLimit)
+ {
+ totalPad = 0;
+ }
+ else
+ {
+ int padPos = end - totalPad;
+ do
+ {
+ padDiff |= (byte)(buf[padPos++] ^ lastByte);
+ }
+ while (padPos < end);
+
+ dummyIndex = totalPad;
+
+ if (padDiff != 0)
+ {
+ totalPad = 0;
+ }
+ }
+
+ // Run some extra dummy checks so the number of checks is always constant
+ {
+ byte[] dummyPad = m_randomData;
+ while (dummyIndex < 256)
+ {
+ padDiff |= (byte)(dummyPad[dummyIndex++] ^ lastByte);
+ }
+ // Ensure the above loop is not eliminated
+ dummyPad[0] ^= padDiff;
+ }
+
+ return totalPad;
+ }
+
+ protected virtual int ChooseExtraPadBlocks(int max)
+ {
+ byte[] random = m_cryptoParams.NonceGenerator.GenerateNonce(4);
+ int x = (int)Pack.LE_To_UInt32(random, 0);
+ int n = Integers.NumberOfTrailingZeros(x);
+ return System.Math.Min(n, max);
+ }
+
+ protected virtual int GetCiphertextLength(int blockSize, int macSize, int maxPadding, int plaintextLength)
+ {
+ int ciphertextLength = plaintextLength;
+
+ // An explicit IV consumes 1 block
+ if (m_useExplicitIV)
+ {
+ ciphertextLength += blockSize;
+ }
+
+ // Leave room for the MAC and (block-aligning) padding
+
+ ciphertextLength += maxPadding;
+
+ if (m_encryptThenMac)
+ {
+ ciphertextLength -= (ciphertextLength % blockSize);
+ ciphertextLength += macSize;
+ }
+ else
+ {
+ ciphertextLength += macSize;
+ ciphertextLength -= (ciphertextLength % blockSize);
+ }
+
+ return ciphertextLength;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs b/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs
new file mode 100644
index 000000000..7df2ed70f
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Interface for block cipher services.</summary>
+ public interface TlsBlockCipherImpl
+ {
+ /// <summary>Set the key to be used by the block cipher implementation supporting this service.</summary>
+ /// <param name="key">array holding the block cipher key.</param>
+ /// <param name="keyOff">offset into the array the key starts at.</param>
+ /// <param name="keyLen">length of the key in the array.</param>
+ /// <exception cref="IOException"/>
+ void SetKey(byte[] key, int keyOff, int keyLen);
+
+ /// <summary>Initialise the parameters for operator.</summary>
+ /// <param name="iv">array holding the initialization vector (IV).</param>
+ /// <param name="ivOff">offset into the array the IV starts at.</param>
+ /// <param name="ivLen">length of the IV in the array.</param>
+ /// <exception cref="IOException">if the parameters are inappropriate.</exception>
+ void Init(byte[] iv, int ivOff, int ivLen);
+
+ /// <summary>Perform the cipher encryption/decryption returning the output in output.</summary>
+ /// <remarks>
+ /// Note: we have to use DoFinal() here as it is the only way to guarantee output from the underlying cipher.
+ /// </remarks>
+ /// <param name="input">array holding input data to the cipher.</param>
+ /// <param name="inputOffset">offset into input array data starts at.</param>
+ /// <param name="inputLength">length of the input data in the array.</param>
+ /// <param name="output">array to hold the cipher output.</param>
+ /// <param name="outputOffset">offset into output array to start saving output.</param>
+ /// <returns>the amount of data written to output.</returns>
+ /// <exception cref="IOException">in case of failure.</exception>
+ int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset);
+
+ /// <summary>Return the blocksize (in bytes) of the underlying block cipher.</summary>
+ /// <returns>the cipher's blocksize.</returns>
+ int GetBlockSize();
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsEncryptor.cs b/crypto/src/tls/crypto/impl/TlsEncryptor.cs
new file mode 100644
index 000000000..6e4ef0c44
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsEncryptor.cs
@@ -0,0 +1,17 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Base interface for an encryptor based on a public key.</summary>
+ public interface TlsEncryptor
+ {
+ /// <summary>Encrypt data from the passed in input array.</summary>
+ /// <param name="input">byte array containing the input data.</param>
+ /// <param name="inOff">offset into input where the data starts.</param>
+ /// <param name="length">the length of the data to encrypt.</param>
+ /// <returns>the encrypted data.</returns>
+ /// <exception cref="IOException"/>
+ byte[] Encrypt(byte[] input, int inOff, int length);
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsImplUtilities.cs b/crypto/src/tls/crypto/impl/TlsImplUtilities.cs
new file mode 100644
index 000000000..db936e6b7
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsImplUtilities.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Useful utility methods.</summary>
+ public abstract class TlsImplUtilities
+ {
+ public static bool IsSsl(TlsCryptoParameters cryptoParams)
+ {
+ return cryptoParams.ServerVersion.IsSsl;
+ }
+
+ public static bool IsTlsV10(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv10.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV10(TlsCryptoParameters cryptoParams)
+ {
+ return IsTlsV10(cryptoParams.ServerVersion);
+ }
+
+ public static bool IsTlsV11(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV11(TlsCryptoParameters cryptoParams)
+ {
+ return IsTlsV11(cryptoParams.ServerVersion);
+ }
+
+ public static bool IsTlsV12(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV12(TlsCryptoParameters cryptoParams)
+ {
+ return IsTlsV12(cryptoParams.ServerVersion);
+ }
+
+ public static bool IsTlsV13(ProtocolVersion version)
+ {
+ return ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(version.GetEquivalentTlsVersion());
+ }
+
+ public static bool IsTlsV13(TlsCryptoParameters cryptoParams)
+ {
+ return IsTlsV13(cryptoParams.ServerVersion);
+ }
+
+ public static byte[] CalculateKeyBlock(TlsCryptoParameters cryptoParams, int length)
+ {
+ SecurityParameters securityParameters = cryptoParams.SecurityParameters;
+ TlsSecret master_secret = securityParameters.MasterSecret;
+ byte[] seed = Arrays.Concatenate(securityParameters.ServerRandom, securityParameters.ClientRandom);
+ return Prf(securityParameters, master_secret, ExporterLabel.key_expansion, seed, length).Extract();
+ }
+
+ public static TlsSecret Prf(SecurityParameters securityParameters, TlsSecret secret, string asciiLabel,
+ byte[] seed, int length)
+ {
+ return secret.DeriveUsingPrf(securityParameters.PrfAlgorithm, asciiLabel, seed, length);
+ }
+
+ public static TlsSecret Prf(TlsCryptoParameters cryptoParams, TlsSecret secret, string asciiLabel, byte[] seed,
+ int length)
+ {
+ return Prf(cryptoParams.SecurityParameters, secret, asciiLabel, seed, length);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsNullCipher.cs b/crypto/src/tls/crypto/impl/TlsNullCipher.cs
new file mode 100644
index 000000000..3ca4951a6
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsNullCipher.cs
@@ -0,0 +1,104 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>The NULL cipher.</summary>
+ public class TlsNullCipher
+ : TlsCipher
+ {
+ protected readonly TlsCryptoParameters m_cryptoParams;
+ protected readonly TlsSuiteHmac m_readMac, m_writeMac;
+
+ /// <exception cref="IOException"/>
+ public TlsNullCipher(TlsCryptoParameters cryptoParams, TlsHmac clientMac, TlsHmac serverMac)
+ {
+ if (TlsImplUtilities.IsTlsV13(cryptoParams))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.m_cryptoParams = cryptoParams;
+
+ int key_block_size = clientMac.MacLength + serverMac.MacLength;
+ byte[] key_block = TlsImplUtilities.CalculateKeyBlock(cryptoParams, key_block_size);
+
+ int offset = 0;
+
+ clientMac.SetKey(key_block, offset, clientMac.MacLength);
+ offset += clientMac.MacLength;
+ serverMac.SetKey(key_block, offset, serverMac.MacLength);
+ offset += serverMac.MacLength;
+
+ if (offset != key_block_size)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (cryptoParams.IsServer)
+ {
+ this.m_writeMac = new TlsSuiteHmac(cryptoParams, serverMac);
+ this.m_readMac = new TlsSuiteHmac(cryptoParams, clientMac);
+ }
+ else
+ {
+ this.m_writeMac = new TlsSuiteHmac(cryptoParams, clientMac);
+ this.m_readMac = new TlsSuiteHmac(cryptoParams, serverMac);
+ }
+ }
+
+ public virtual int GetCiphertextDecodeLimit(int plaintextLimit)
+ {
+ return plaintextLimit + m_writeMac.Size;
+ }
+
+ public virtual int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
+ {
+ return plaintextLength + m_writeMac.Size;
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - m_writeMac.Size;
+ }
+
+ public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+ int headerAllocation, byte[] plaintext, int offset, int len)
+ {
+ byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext, offset, len);
+ byte[] ciphertext = new byte[headerAllocation + len + mac.Length];
+ Array.Copy(plaintext, offset, ciphertext, headerAllocation, len);
+ Array.Copy(mac, 0, ciphertext, headerAllocation + len, mac.Length);
+ return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, contentType);
+ }
+
+ public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
+ byte[] ciphertext, int offset, int len)
+ {
+ int macSize = m_readMac.Size;
+ if (len < macSize)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int macInputLen = len - macSize;
+
+ byte[] expectedMac = m_readMac.CalculateMac(seqNo, recordType, ciphertext, offset, macInputLen);
+
+ bool badMac = !TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext, offset + macInputLen);
+ if (badMac)
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ return new TlsDecodeResult(ciphertext, offset, macInputLen, recordType);
+ }
+
+ public virtual void RekeyDecoder()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual void RekeyEncoder()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual bool UsesOpaqueRecordType
+ {
+ get { return false; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs b/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs
new file mode 100644
index 000000000..9f43f4382
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs
@@ -0,0 +1,121 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest.</summary>
+ public class TlsSuiteHmac
+ : TlsSuiteMac
+ {
+ protected static int GetMacSize(TlsCryptoParameters cryptoParams, TlsMac mac)
+ {
+ int macSize = mac.MacLength;
+ if (cryptoParams.SecurityParameters.IsTruncatedHmac)
+ {
+ macSize = System.Math.Min(macSize, 10);
+ }
+ return macSize;
+ }
+
+ protected readonly TlsCryptoParameters m_cryptoParams;
+ protected readonly TlsHmac m_mac;
+ protected readonly int m_digestBlockSize;
+ protected readonly int m_digestOverhead;
+ protected readonly int m_macSize;
+
+ /// <summary>Generate a new instance of a TlsMac.</summary>
+ /// <param name="cryptoParams">the TLS client context specific crypto parameters.</param>
+ /// <param name="mac">The MAC to use.</param>
+ public TlsSuiteHmac(TlsCryptoParameters cryptoParams, TlsHmac mac)
+ {
+ this.m_cryptoParams = cryptoParams;
+ this.m_mac = mac;
+ this.m_macSize = GetMacSize(cryptoParams, mac);
+ this.m_digestBlockSize = mac.InternalBlockSize;
+
+ // TODO This should check the actual algorithm, not assume based on the digest size
+ if (TlsImplUtilities.IsSsl(cryptoParams) && mac.MacLength == 20)
+ {
+ /*
+ * NOTE: For the SSL 3.0 MAC with SHA-1, the secret + input pad is not block-aligned.
+ */
+ this.m_digestOverhead = 4;
+ }
+ else
+ {
+ this.m_digestOverhead = m_digestBlockSize / 8;
+ }
+ }
+
+ public virtual int Size
+ {
+ get { return m_macSize; }
+ }
+
+ public virtual byte[] CalculateMac(long seqNo, short type, byte[] msg, int msgOff, int msgLen)
+ {
+ ProtocolVersion serverVersion = m_cryptoParams.ServerVersion;
+ bool isSsl = serverVersion.IsSsl;
+
+ byte[] macHeader = new byte[isSsl ? 11 : 13];
+ TlsUtilities.WriteUint64(seqNo, macHeader, 0);
+ TlsUtilities.WriteUint8(type, macHeader, 8);
+ if (!isSsl)
+ {
+ TlsUtilities.WriteVersion(serverVersion, macHeader, 9);
+ }
+ TlsUtilities.WriteUint16(msgLen, macHeader, macHeader.Length - 2);
+
+ m_mac.Update(macHeader, 0, macHeader.Length);
+ m_mac.Update(msg, msgOff, msgLen);
+
+ return Truncate(m_mac.CalculateMac());
+ }
+
+ public virtual byte[] CalculateMacConstantTime(long seqNo, short type, byte[] msg, int msgOff, int msgLen,
+ int fullLength, byte[] dummyData)
+ {
+ /*
+ * Actual MAC only calculated on 'length' bytes...
+ */
+ byte[] result = CalculateMac(seqNo, type, msg, msgOff, msgLen);
+
+ /*
+ * ...but ensure a constant number of complete digest blocks are processed (as many as would
+ * be needed for 'fullLength' bytes of input).
+ */
+ int headerLength = TlsImplUtilities.IsSsl(m_cryptoParams) ? 11 : 13;
+
+ // How many extra full blocks do we need to calculate?
+ int extra = GetDigestBlockCount(headerLength + fullLength) - GetDigestBlockCount(headerLength + msgLen);
+
+ while (--extra >= 0)
+ {
+ m_mac.Update(dummyData, 0, m_digestBlockSize);
+ }
+
+ // One more byte in case the implementation is "lazy" about processing blocks
+ m_mac.Update(dummyData, 0, 1);
+ m_mac.Reset();
+
+ return result;
+ }
+
+ protected virtual int GetDigestBlockCount(int inputLength)
+ {
+ // NOTE: The input pad for HMAC is always a full digest block
+
+ // NOTE: This calculation assumes a minimum of 1 pad byte
+ return (inputLength + m_digestOverhead) / m_digestBlockSize;
+ }
+
+ protected virtual byte[] Truncate(byte[] bs)
+ {
+ if (bs.Length <= m_macSize)
+ return bs;
+
+ return Arrays.CopyOf(bs, m_macSize);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/TlsSuiteMac.cs b/crypto/src/tls/crypto/impl/TlsSuiteMac.cs
new file mode 100644
index 000000000..6e4942928
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/TlsSuiteMac.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl
+{
+ /// <summary>Base interface for a generic TLS MAC implementation for use with a bulk cipher.</summary>
+ public interface TlsSuiteMac
+ {
+ /// <summary>Return the output length (in bytes) of this MAC.</summary>
+ /// <returns>The output length of this MAC.</returns>
+ int Size { get; }
+
+ /// <summary>Calculate the MAC for some given data.</summary>
+ /// <param name="seqNo">The sequence number of the record.</param>
+ /// <param name="type">The content type of the message.</param>
+ /// <param name="message">A byte array containing the message.</param>
+ /// <param name="offset">The number of bytes to skip, before the message starts.</param>
+ /// <param name="length">The length of the message.</param>
+ /// <returns>A new byte array containing the MAC value.</returns>
+ byte[] CalculateMac(long seqNo, short type, byte[] message, int offset, int length);
+
+ /// <summary>Constant time calculation of the MAC for some given data with a given expected length.</summary>
+ /// <param name="seqNo">The sequence number of the record.</param>
+ /// <param name="type">The content type of the message.</param>
+ /// <param name="message">A byte array containing the message.</param>
+ /// <param name="offset">The number of bytes to skip, before the message starts.</param>
+ /// <param name="length">The length of the message.</param>
+ /// <param name="expectedLength">The expected length of the full message.</param>
+ /// <param name="randomData">Random data for padding out the MAC calculation if required.</param>
+ /// <returns>A new byte array containing the MAC value.</returns>
+ byte[] CalculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length,
+ int expectedLength, byte[] randomData);
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs b/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
new file mode 100644
index 000000000..8d801ed7a
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
@@ -0,0 +1,124 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public sealed class BcChaCha20Poly1305
+ : TlsAeadCipherImpl
+ {
+ private static readonly byte[] Zeroes = new byte[15];
+
+ private readonly ChaCha7539Engine m_cipher = new ChaCha7539Engine();
+ private readonly Poly1305 m_mac = new Poly1305();
+
+ private readonly bool m_isEncrypting;
+
+ private int m_additionalDataLength;
+
+ public BcChaCha20Poly1305(bool isEncrypting)
+ {
+ this.m_isEncrypting = isEncrypting;
+ }
+
+ public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
+ {
+ if (m_isEncrypting)
+ {
+ int ciphertextLength = inputLength;
+
+ m_cipher.ProcessBytes(input, inputOffset, inputLength, output, outputOffset);
+ int outputLength = inputLength;
+
+ if (ciphertextLength != outputLength)
+ throw new InvalidOperationException();
+
+ UpdateMac(output, outputOffset, ciphertextLength);
+
+ byte[] lengths = new byte[16];
+ Pack.UInt64_To_LE((ulong)m_additionalDataLength, lengths, 0);
+ Pack.UInt64_To_LE((ulong)ciphertextLength, lengths, 8);
+ m_mac.BlockUpdate(lengths, 0, 16);
+
+ m_mac.DoFinal(output, outputOffset + ciphertextLength);
+
+ return ciphertextLength + 16;
+ }
+ else
+ {
+ int ciphertextLength = inputLength - 16;
+
+ UpdateMac(input, inputOffset, ciphertextLength);
+
+ byte[] expectedMac = new byte[16];
+ Pack.UInt64_To_LE((ulong)m_additionalDataLength, expectedMac, 0);
+ Pack.UInt64_To_LE((ulong)ciphertextLength, expectedMac, 8);
+ m_mac.BlockUpdate(expectedMac, 0, 16);
+ m_mac.DoFinal(expectedMac, 0);
+
+ bool badMac = !TlsUtilities.ConstantTimeAreEqual(16, expectedMac, 0, input, inputOffset + ciphertextLength);
+ if (badMac)
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ m_cipher.ProcessBytes(input, inputOffset, ciphertextLength, output, outputOffset);
+ int outputLength = ciphertextLength;
+
+ if (ciphertextLength != outputLength)
+ throw new InvalidOperationException();
+
+ return ciphertextLength;
+ }
+ }
+
+ public int GetOutputSize(int inputLength)
+ {
+ return m_isEncrypting ? inputLength + 16 : inputLength - 16;
+ }
+
+ public void Init(byte[] nonce, int macSize, byte[] additionalData)
+ {
+ if (nonce == null || nonce.Length != 12 || macSize != 16)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ m_cipher.Init(m_isEncrypting, new ParametersWithIV(null, nonce));
+ InitMac();
+ if (additionalData == null)
+ {
+ this.m_additionalDataLength = 0;
+ }
+ else
+ {
+ this.m_additionalDataLength = additionalData.Length;
+ UpdateMac(additionalData, 0, additionalData.Length);
+ }
+ }
+
+ public void SetKey(byte[] key, int keyOff, int keyLen)
+ {
+ KeyParameter cipherKey = new KeyParameter(key, keyOff, keyLen);
+ m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes, 0, 12));
+ }
+
+ private void InitMac()
+ {
+ byte[] firstBlock = new byte[64];
+ m_cipher.ProcessBytes(firstBlock, 0, 64, firstBlock, 0);
+ m_mac.Init(new KeyParameter(firstBlock, 0, 32));
+ Array.Clear(firstBlock, 0, firstBlock.Length);
+ }
+
+ private void UpdateMac(byte[] buf, int off, int len)
+ {
+ m_mac.BlockUpdate(buf, off, len);
+
+ int partial = len % 16;
+ if (partial != 0)
+ {
+ m_mac.BlockUpdate(Zeroes, 0, 16 - partial);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedAgreement.cs b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedAgreement.cs
new file mode 100644
index 000000000..15944cd89
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedAgreement.cs
@@ -0,0 +1,112 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summay>Credentialed class generating agreed secrets from a peer's public key for our end of the TLS connection
+ /// using the BC light-weight API.</summay>
+ public class BcDefaultTlsCredentialedAgreement
+ : TlsCredentialedAgreement
+ {
+ protected readonly TlsCredentialedAgreement m_agreementCredentials;
+
+ public BcDefaultTlsCredentialedAgreement(BcTlsCrypto crypto, Certificate certificate,
+ AsymmetricKeyParameter privateKey)
+ {
+ if (crypto == null)
+ throw new ArgumentNullException("crypto");
+ 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 (privateKey is DHPrivateKeyParameters)
+ {
+ this.m_agreementCredentials = new DHCredentialedAgreement(crypto, certificate,
+ (DHPrivateKeyParameters)privateKey);
+ }
+ else if (privateKey is ECPrivateKeyParameters)
+ {
+ this.m_agreementCredentials = new ECCredentialedAgreement(crypto, certificate,
+ (ECPrivateKeyParameters)privateKey);
+ }
+ else
+ {
+ throw new ArgumentException("'privateKey' type not supported: " + Platform.GetTypeName(privateKey));
+ }
+ }
+
+ public virtual Certificate Certificate
+ {
+ get { return m_agreementCredentials.Certificate; }
+ }
+
+ public virtual TlsSecret GenerateAgreement(TlsCertificate peerCertificate)
+ {
+ return m_agreementCredentials.GenerateAgreement(peerCertificate);
+ }
+
+ private sealed class DHCredentialedAgreement
+ : TlsCredentialedAgreement
+ {
+ private readonly BcTlsCrypto m_crypto;
+ private readonly Certificate m_certificate;
+ private readonly DHPrivateKeyParameters m_privateKey;
+
+ internal DHCredentialedAgreement(BcTlsCrypto crypto, Certificate certificate,
+ DHPrivateKeyParameters privateKey)
+ {
+ this.m_crypto = crypto;
+ this.m_certificate = certificate;
+ this.m_privateKey = privateKey;
+ }
+
+ public TlsSecret GenerateAgreement(TlsCertificate peerCertificate)
+ {
+ BcTlsCertificate bcCert = BcTlsCertificate.Convert(m_crypto, peerCertificate);
+ DHPublicKeyParameters peerPublicKey = bcCert.GetPubKeyDH();
+ return BcTlsDHDomain.CalculateDHAgreement(m_crypto, m_privateKey, peerPublicKey, false);
+ }
+
+ public Certificate Certificate
+ {
+ get { return m_certificate; }
+ }
+ }
+
+ private sealed class ECCredentialedAgreement
+ : TlsCredentialedAgreement
+ {
+ private readonly BcTlsCrypto m_crypto;
+ private readonly Certificate m_certificate;
+ private readonly ECPrivateKeyParameters m_privateKey;
+
+ internal ECCredentialedAgreement(BcTlsCrypto crypto, Certificate certificate,
+ ECPrivateKeyParameters privateKey)
+ {
+ this.m_crypto = crypto;
+ this.m_certificate = certificate;
+ this.m_privateKey = privateKey;
+ }
+
+ public TlsSecret GenerateAgreement(TlsCertificate peerCertificate)
+ {
+ BcTlsCertificate bcCert = BcTlsCertificate.Convert(m_crypto, peerCertificate);
+ ECPublicKeyParameters peerPublicKey = bcCert.GetPubKeyEC();
+ return BcTlsECDomain.CalculateBasicAgreement(m_crypto, m_privateKey, peerPublicKey);
+ }
+
+ public Certificate Certificate
+ {
+ get { return m_certificate; }
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs
new file mode 100644
index 000000000..b0e9f125e
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedDecryptor.cs
@@ -0,0 +1,139 @@
+using System;
+
+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;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Credentialed class decrypting RSA encrypted secrets sent from a peer for our end of the TLS connection
+ /// using the BC light-weight API.</summary>
+ public class BcDefaultTlsCredentialedDecryptor
+ : TlsCredentialedDecryptor
+ {
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly Certificate m_certificate;
+ protected readonly AsymmetricKeyParameter m_privateKey;
+
+ public BcDefaultTlsCredentialedDecryptor(BcTlsCrypto crypto, Certificate certificate,
+ AsymmetricKeyParameter privateKey)
+ {
+ if (crypto == null)
+ throw new ArgumentNullException("crypto");
+ 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 (privateKey is RsaKeyParameters)
+ {
+ }
+ else
+ {
+ throw new ArgumentException("'privateKey' type not supported: " + Platform.GetTypeName(privateKey));
+ }
+
+ this.m_crypto = crypto;
+ this.m_certificate = certificate;
+ this.m_privateKey = privateKey;
+ }
+
+ public virtual Certificate Certificate
+ {
+ get { return m_certificate; }
+ }
+
+ public virtual TlsSecret Decrypt(TlsCryptoParameters cryptoParams, byte[] ciphertext)
+ {
+ // TODO Keep only the decryption itself here - move error handling outside
+ return SafeDecryptPreMasterSecret(cryptoParams, (RsaKeyParameters)m_privateKey, ciphertext);
+ }
+
+ /*
+ * TODO[tls-ops] Probably need to make RSA encryption/decryption into TlsCrypto functions so
+ * that users can implement "generic" encryption credentials externally
+ */
+ protected virtual TlsSecret SafeDecryptPreMasterSecret(TlsCryptoParameters cryptoParams,
+ RsaKeyParameters rsaServerPrivateKey, byte[] encryptedPreMasterSecret)
+ {
+ SecureRandom secureRandom = m_crypto.SecureRandom;
+
+ /*
+ * RFC 5246 7.4.7.1.
+ */
+ ProtocolVersion expectedVersion = cryptoParams.RsaPreMasterSecretVersion;
+
+ // 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];
+ secureRandom.NextBytes(fallback);
+
+ byte[] M = Arrays.Clone(fallback);
+ try
+ {
+ Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback);
+ encoding.Init(false, new ParametersWithRandom(rsaServerPrivateKey, 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.legacy_version is TLS 1.1 or higher, server implementations MUST check the
+ * version number [..].
+ */
+ if (versionNumberCheckDisabled && !TlsImplUtilities.IsTlsV11(expectedVersion))
+ {
+ /*
+ * If the version number is TLS 1.0 or earlier, server implementations SHOULD check the
+ * version number, but MAY have a configuration option to disable the check.
+ */
+ }
+ else
+ {
+ /*
+ * Compare the version number in the decrypted Pre-Master-Secret with the legacy_version
+ * field from the ClientHello. If they don't match, continue the handshake with the
+ * randomly generated 'fallback' value.
+ *
+ * NOTE: The comparison and replacement must be constant-time.
+ */
+ int mask = (expectedVersion.MajorVersion ^ (M[0] & 0xFF))
+ | (expectedVersion.MinorVersion ^ (M[1] & 0xFF));
+
+ // 'mask' will be all 1s if the versions matched, or else all 0s.
+ mask = (mask - 1) >> 31;
+
+ for (int i = 0; i < 48; i++)
+ {
+ M[i] = (byte)((M[i] & mask) | (fallback[i] & ~mask));
+ }
+ }
+
+ return m_crypto.CreateSecret(M);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.cs b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.cs
new file mode 100644
index 000000000..6db84cdd8
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.cs
@@ -0,0 +1,85 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Credentialed class for generating signatures based on the use of primitives from the BC light-weight API.</summary>
+ public class BcDefaultTlsCredentialedSigner
+ : DefaultTlsCredentialedSigner
+ {
+ private static BcTlsCertificate GetEndEntity(BcTlsCrypto crypto, Certificate certificate)
+ {
+ if (certificate == null || certificate.IsEmpty)
+ throw new ArgumentException("No certificate");
+
+ return BcTlsCertificate.Convert(crypto, certificate.GetCertificateAt(0));
+ }
+
+ private static TlsSigner MakeSigner(BcTlsCrypto crypto, AsymmetricKeyParameter privateKey,
+ Certificate certificate, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ TlsSigner signer;
+ if (privateKey is RsaKeyParameters)
+ {
+ RsaKeyParameters privKeyRsa = (RsaKeyParameters)privateKey;
+
+ if (signatureAndHashAlgorithm != null)
+ {
+ int signatureScheme = SignatureScheme.From(signatureAndHashAlgorithm);
+ if (SignatureScheme.IsRsaPss(signatureScheme))
+ {
+ return new BcTlsRsaPssSigner(crypto, privKeyRsa, signatureScheme);
+ }
+ }
+
+ RsaKeyParameters pubKeyRsa = GetEndEntity(crypto, certificate).GetPubKeyRsa();
+
+ signer = new BcTlsRsaSigner(crypto, privKeyRsa, pubKeyRsa);
+ }
+ else if (privateKey is DsaPrivateKeyParameters)
+ {
+ signer = new BcTlsDsaSigner(crypto, (DsaPrivateKeyParameters)privateKey);
+ }
+ else if (privateKey is ECPrivateKeyParameters)
+ {
+ ECPrivateKeyParameters privKeyEC = (ECPrivateKeyParameters)privateKey;
+
+ if (signatureAndHashAlgorithm != null)
+ {
+ int signatureScheme = SignatureScheme.From(signatureAndHashAlgorithm);
+ if (SignatureScheme.IsECDsa(signatureScheme))
+ {
+ return new BcTlsECDsa13Signer(crypto, privKeyEC, signatureScheme);
+ }
+ }
+
+ signer = new BcTlsECDsaSigner(crypto, privKeyEC);
+ }
+ else if (privateKey is Ed25519PrivateKeyParameters)
+ {
+ signer = new BcTlsEd25519Signer(crypto, (Ed25519PrivateKeyParameters)privateKey);
+ }
+ else if (privateKey is Ed448PrivateKeyParameters)
+ {
+ signer = new BcTlsEd448Signer(crypto, (Ed448PrivateKeyParameters)privateKey);
+ }
+ else
+ {
+ throw new ArgumentException("'privateKey' type not supported: " + Platform.GetTypeName(privateKey));
+ }
+
+ return signer;
+ }
+
+ public BcDefaultTlsCredentialedSigner(TlsCryptoParameters cryptoParams, BcTlsCrypto crypto,
+ AsymmetricKeyParameter privateKey, Certificate certificate,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ : base(cryptoParams, MakeSigner(crypto, privateKey, certificate, signatureAndHashAlgorithm), certificate,
+ signatureAndHashAlgorithm)
+ {
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
new file mode 100644
index 000000000..df2ccd2c1
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
@@ -0,0 +1,112 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>HMAC implementation based on original internet draft for HMAC (RFC 2104).</summary>
+ /// <remarks>
+ /// The difference is that padding is concatenated versus XORed with the key, e.g:
+ /// <code>H(K + opad, H(K + ipad, text))</code>
+ /// </remarks>
+ internal class BcSsl3Hmac
+ : TlsHmac
+ {
+ private const byte IPAD_BYTE = (byte)0x36;
+ private const byte OPAD_BYTE = (byte)0x5C;
+
+ private static readonly byte[] IPAD = GenPad(IPAD_BYTE, 48);
+ private static readonly byte[] OPAD = GenPad(OPAD_BYTE, 48);
+
+ private readonly IDigest m_digest;
+ private readonly int m_padLength;
+
+ private byte[] m_secret;
+
+ /// <summary>Base constructor for one of the standard digest algorithms for which the byteLength is known.
+ /// </summary>
+ /// <remarks>
+ /// Behaviour is undefined for digests other than MD5 or SHA1.
+ /// </remarks>
+ /// <param name="digest">the digest.</param>
+ internal BcSsl3Hmac(IDigest digest)
+ {
+ this.m_digest = digest;
+
+ if (digest.GetDigestSize() == 20)
+ {
+ this.m_padLength = 40;
+ }
+ else
+ {
+ this.m_padLength = 48;
+ }
+ }
+
+ public virtual void SetKey(byte[] key, int keyOff, int keyLen)
+ {
+ this.m_secret = TlsUtilities.CopyOfRangeExact(key, keyOff, keyOff + keyLen);
+
+ Reset();
+ }
+
+ public virtual void Update(byte[] input, int inOff, int len)
+ {
+ m_digest.BlockUpdate(input, inOff, len);
+ }
+
+ public virtual byte[] CalculateMac()
+ {
+ byte[] result = new byte[m_digest.GetDigestSize()];
+ DoFinal(result, 0);
+ return result;
+ }
+
+ public virtual void CalculateMac(byte[] output, int outOff)
+ {
+ DoFinal(output, outOff);
+ }
+
+ public virtual int InternalBlockSize
+ {
+ get { return m_digest.GetByteLength(); }
+ }
+
+ public virtual int MacLength
+ {
+ get { return m_digest.GetDigestSize(); }
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public virtual void Reset()
+ {
+ m_digest.Reset();
+ m_digest.BlockUpdate(m_secret, 0, m_secret.Length);
+ m_digest.BlockUpdate(IPAD, 0, m_padLength);
+ }
+
+ private void DoFinal(byte[] output, int outOff)
+ {
+ byte[] tmp = new byte[m_digest.GetDigestSize()];
+ m_digest.DoFinal(tmp, 0);
+
+ m_digest.BlockUpdate(m_secret, 0, m_secret.Length);
+ m_digest.BlockUpdate(OPAD, 0, m_padLength);
+ m_digest.BlockUpdate(tmp, 0, tmp.Length);
+
+ m_digest.DoFinal(output, outOff);
+
+ Reset();
+ }
+
+ private static byte[] GenPad(byte b, int count)
+ {
+ byte[] padding = new byte[count];
+ Arrays.Fill(padding, b);
+ return padding;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
new file mode 100644
index 000000000..ae05a1664
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsAeadCipherImpl
+ : TlsAeadCipherImpl
+ {
+ private readonly bool m_isEncrypting;
+ private readonly IAeadBlockCipher m_cipher;
+
+ private KeyParameter key;
+
+ internal BcTlsAeadCipherImpl(IAeadBlockCipher cipher, bool isEncrypting)
+ {
+ this.m_cipher = cipher;
+ this.m_isEncrypting = isEncrypting;
+ }
+
+ public void SetKey(byte[] key, int keyOff, int keyLen)
+ {
+ this.key = new KeyParameter(key, keyOff, keyLen);
+ }
+
+ public void Init(byte[] nonce, int macSize, byte[] additionalData)
+ {
+ m_cipher.Init(m_isEncrypting, new AeadParameters(key, macSize * 8, nonce, additionalData));
+ }
+
+ public int GetOutputSize(int inputLength)
+ {
+ return m_cipher.GetOutputSize(inputLength);
+ }
+
+ public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
+ {
+ int len = m_cipher.ProcessBytes(input, inputOffset, inputLength, output, outputOffset);
+
+ try
+ {
+ len += m_cipher.DoFinal(output, outputOffset + len);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
+ }
+
+ return len;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs
new file mode 100644
index 000000000..b51d38c0d
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs
@@ -0,0 +1,49 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsBlockCipherImpl
+ : TlsBlockCipherImpl
+ {
+ private readonly bool m_isEncrypting;
+ private readonly IBlockCipher m_cipher;
+
+ private KeyParameter key;
+
+ internal BcTlsBlockCipherImpl(IBlockCipher cipher, bool isEncrypting)
+ {
+ this.m_cipher = cipher;
+ this.m_isEncrypting = isEncrypting;
+ }
+
+ public void SetKey(byte[] key, int keyOff, int keyLen)
+ {
+ this.key = new KeyParameter(key, keyOff, keyLen);
+ }
+
+ public void Init(byte[] iv, int ivOff, int ivLen)
+ {
+ m_cipher.Init(m_isEncrypting, new ParametersWithIV(key, iv, ivOff, ivLen));
+ }
+
+ public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
+ {
+ int blockSize = m_cipher.GetBlockSize();
+
+ for (int i = 0; i < inputLength; i += blockSize)
+ {
+ m_cipher.ProcessBlock(input, inputOffset + i, output, outputOffset + i);
+ }
+
+ return inputLength;
+ }
+
+ public int GetBlockSize()
+ {
+ return m_cipher.GetBlockSize();
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs
new file mode 100644
index 000000000..e1243087d
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs
@@ -0,0 +1,484 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for a single X.509 certificate based on the BC light-weight API.</summary>
+ public class BcTlsCertificate
+ : TlsCertificate
+ {
+ /// <exception cref="IOException"/>
+ public static BcTlsCertificate Convert(BcTlsCrypto crypto, TlsCertificate certificate)
+ {
+ if (certificate is BcTlsCertificate)
+ return (BcTlsCertificate)certificate;
+
+ return new BcTlsCertificate(crypto, certificate.GetEncoded());
+ }
+
+ /// <exception cref="IOException"/>
+ public static X509CertificateStructure ParseCertificate(byte[] encoding)
+ {
+ try
+ {
+ return X509CertificateStructure.GetInstance(encoding);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate, e);
+ }
+ }
+
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly X509CertificateStructure m_certificate;
+
+ protected DHPublicKeyParameters m_pubKeyDH = null;
+ protected ECPublicKeyParameters m_pubKeyEC = null;
+ protected Ed25519PublicKeyParameters m_pubKeyEd25519 = null;
+ protected Ed448PublicKeyParameters m_pubKeyEd448 = null;
+ protected RsaKeyParameters m_pubKeyRsa = null;
+
+ /// <exception cref="IOException"/>
+ public BcTlsCertificate(BcTlsCrypto crypto, byte[] encoding)
+ : this(crypto, ParseCertificate(encoding))
+ {
+ }
+
+ public BcTlsCertificate(BcTlsCrypto crypto, X509CertificateStructure certificate)
+ {
+ this.m_crypto = crypto;
+ this.m_certificate = certificate;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsVerifier CreateVerifier(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ case SignatureAlgorithm.ed25519:
+ case SignatureAlgorithm.ed448:
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return CreateVerifier(SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm));
+ }
+
+ ValidateKeyUsage(KeyUsage.DigitalSignature);
+
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ ValidateRsa_Pkcs1();
+ return new BcTlsRsaVerifier(m_crypto, GetPubKeyRsa());
+
+ case SignatureAlgorithm.dsa:
+ return new BcTlsDsaVerifier(m_crypto, GetPubKeyDss());
+
+ case SignatureAlgorithm.ecdsa:
+ return new BcTlsECDsaVerifier(m_crypto, GetPubKeyEC());
+
+ default:
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsVerifier CreateVerifier(int signatureScheme)
+ {
+ ValidateKeyUsage(KeyUsage.DigitalSignature);
+
+ switch (signatureScheme)
+ {
+ case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256:
+ case SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384:
+ case SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512:
+ case SignatureScheme.ecdsa_secp256r1_sha256:
+ case SignatureScheme.ecdsa_secp384r1_sha384:
+ case SignatureScheme.ecdsa_secp521r1_sha512:
+ case SignatureScheme.ecdsa_sha1:
+ return new BcTlsECDsa13Verifier(m_crypto, GetPubKeyEC(), signatureScheme);
+
+ case SignatureScheme.ed25519:
+ return new BcTlsEd25519Verifier(m_crypto, GetPubKeyEd25519());
+
+ case SignatureScheme.ed448:
+ return new BcTlsEd448Verifier(m_crypto, GetPubKeyEd448());
+
+ case SignatureScheme.rsa_pkcs1_sha1:
+ case SignatureScheme.rsa_pkcs1_sha256:
+ case SignatureScheme.rsa_pkcs1_sha384:
+ case SignatureScheme.rsa_pkcs1_sha512:
+ {
+ ValidateRsa_Pkcs1();
+ return new BcTlsRsaVerifier(m_crypto, GetPubKeyRsa());
+ }
+
+ case SignatureScheme.rsa_pss_pss_sha256:
+ case SignatureScheme.rsa_pss_pss_sha384:
+ case SignatureScheme.rsa_pss_pss_sha512:
+ {
+ ValidateRsa_Pss_Pss(SignatureScheme.GetSignatureAlgorithm(signatureScheme));
+ return new BcTlsRsaPssVerifier(m_crypto, GetPubKeyRsa(), signatureScheme);
+ }
+
+ case SignatureScheme.rsa_pss_rsae_sha256:
+ case SignatureScheme.rsa_pss_rsae_sha384:
+ case SignatureScheme.rsa_pss_rsae_sha512:
+ {
+ ValidateRsa_Pss_Rsae();
+ return new BcTlsRsaPssVerifier(m_crypto, GetPubKeyRsa(), signatureScheme);
+ }
+
+ // TODO[RFC 8998]
+ //case SignatureScheme.sm2sig_sm3:
+ // return new BcTlsSM2Verifier(m_crypto, GetPubKeyEC(), Strings.ToByteArray("TLSv1.3+GM+Cipher+Suite"));
+
+ default:
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual byte[] GetEncoded()
+ {
+ return m_certificate.GetEncoded(Asn1Encodable.Der);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual byte[] GetExtension(DerObjectIdentifier extensionOid)
+ {
+ X509Extensions extensions = m_certificate.TbsCertificate.Extensions;
+ if (extensions != null)
+ {
+ X509Extension extension = extensions.GetExtension(extensionOid);
+ if (extension != null)
+ {
+ return Arrays.Clone(extension.Value.GetOctets());
+ }
+ }
+ return null;
+ }
+
+ public virtual BigInteger SerialNumber
+ {
+ get { return m_certificate.SerialNumber.Value; }
+ }
+
+ public virtual string SigAlgOid
+ {
+ get { return m_certificate.SignatureAlgorithm.Algorithm.Id; }
+ }
+
+ public virtual Asn1Encodable GetSigAlgParams()
+ {
+ return m_certificate.SignatureAlgorithm.Parameters;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual short GetLegacySignatureAlgorithm()
+ {
+ AsymmetricKeyParameter publicKey = GetPublicKey();
+ if (publicKey.IsPrivate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ if (!SupportsKeyUsage(KeyUsage.DigitalSignature))
+ return -1;
+
+ /*
+ * 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)
+ return SignatureAlgorithm.rsa;
+
+ /*
+ * 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)
+ return SignatureAlgorithm.dsa;
+
+ /*
+ * 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)
+ {
+ // TODO Check the curve and point format
+ return SignatureAlgorithm.ecdsa;
+ }
+
+ return -1;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual DHPublicKeyParameters GetPubKeyDH()
+ {
+ try
+ {
+ return (DHPublicKeyParameters)GetPublicKey();
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual DsaPublicKeyParameters GetPubKeyDss()
+ {
+ try
+ {
+ return (DsaPublicKeyParameters)GetPublicKey();
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual ECPublicKeyParameters GetPubKeyEC()
+ {
+ try
+ {
+ return (ECPublicKeyParameters)GetPublicKey();
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual Ed25519PublicKeyParameters GetPubKeyEd25519()
+ {
+ try
+ {
+ return (Ed25519PublicKeyParameters)GetPublicKey();
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual Ed448PublicKeyParameters GetPubKeyEd448()
+ {
+ try
+ {
+ return (Ed448PublicKeyParameters)GetPublicKey();
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual RsaKeyParameters GetPubKeyRsa()
+ {
+ try
+ {
+ return (RsaKeyParameters)GetPublicKey();
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual bool SupportsSignatureAlgorithm(short signatureAlgorithm)
+ {
+ return SupportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.DigitalSignature);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual bool SupportsSignatureAlgorithmCA(short signatureAlgorithm)
+ {
+ return SupportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.KeyCertSign);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsCertificate CheckUsageInRole(int connectionEnd, int tlsCertificateRole)
+ {
+ switch (tlsCertificateRole)
+ {
+ case TlsCertificateRole.DH:
+ {
+ ValidateKeyUsage(KeyUsage.KeyAgreement);
+ this.m_pubKeyDH = GetPubKeyDH();
+ return this;
+ }
+ case TlsCertificateRole.ECDH:
+ {
+ ValidateKeyUsage(KeyUsage.KeyAgreement);
+ this.m_pubKeyEC = GetPubKeyEC();
+ return this;
+ }
+ }
+
+ if (connectionEnd == ConnectionEnd.server)
+ {
+ switch (tlsCertificateRole)
+ {
+ case TlsCertificateRole.RsaEncryption:
+ {
+ ValidateKeyUsage(KeyUsage.KeyEncipherment);
+ this.m_pubKeyRsa = GetPubKeyRsa();
+ return this;
+ }
+ case TlsCertificateRole.Sm2Encryption:
+ {
+ ValidateKeyUsage(KeyUsage.KeyEncipherment);
+ this.m_pubKeyEC = GetPubKeyEC();
+ return this;
+ }
+ }
+ }
+
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual AsymmetricKeyParameter GetPublicKey()
+ {
+ SubjectPublicKeyInfo keyInfo = m_certificate.SubjectPublicKeyInfo;
+ try
+ {
+ return PublicKeyFactory.CreateKey(keyInfo);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
+ }
+ }
+
+ protected virtual bool SupportsKeyUsage(int keyUsageBits)
+ {
+ X509Extensions exts = m_certificate.TbsCertificate.Extensions;
+ if (exts != null)
+ {
+ KeyUsage ku = KeyUsage.FromExtensions(exts);
+ if (ku != null)
+ {
+ int bits = ku.GetBytes()[0] & 0xff;
+ if ((bits & keyUsageBits) != keyUsageBits)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected virtual bool SupportsRsa_Pkcs1()
+ {
+ AlgorithmIdentifier pubKeyAlgID = m_certificate.SubjectPublicKeyInfo.AlgorithmID;
+ return RsaUtilities.SupportsPkcs1(pubKeyAlgID);
+ }
+
+ protected virtual bool SupportsRsa_Pss_Pss(short signatureAlgorithm)
+ {
+ AlgorithmIdentifier pubKeyAlgID = m_certificate.SubjectPublicKeyInfo.AlgorithmID;
+ return RsaUtilities.SupportsPss_Pss(signatureAlgorithm, pubKeyAlgID);
+ }
+
+ protected virtual bool SupportsRsa_Pss_Rsae()
+ {
+ AlgorithmIdentifier pubKeyAlgID = m_certificate.SubjectPublicKeyInfo.AlgorithmID;
+ return RsaUtilities.SupportsPss_Rsae(pubKeyAlgID);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual bool SupportsSignatureAlgorithm(short signatureAlgorithm, int keyUsage)
+ {
+ if (!SupportsKeyUsage(keyUsage))
+ return false;
+
+ AsymmetricKeyParameter publicKey = GetPublicKey();
+
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ return SupportsRsa_Pkcs1()
+ && publicKey is RsaKeyParameters;
+
+ case SignatureAlgorithm.dsa:
+ return publicKey is DsaPublicKeyParameters;
+
+ case SignatureAlgorithm.ecdsa:
+ case SignatureAlgorithm.ecdsa_brainpoolP256r1tls13_sha256:
+ case SignatureAlgorithm.ecdsa_brainpoolP384r1tls13_sha384:
+ case SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512:
+ return publicKey is ECPublicKeyParameters;
+
+ case SignatureAlgorithm.ed25519:
+ return publicKey is Ed25519PublicKeyParameters;
+
+ case SignatureAlgorithm.ed448:
+ return publicKey is Ed448PublicKeyParameters;
+
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return SupportsRsa_Pss_Rsae()
+ && publicKey is RsaKeyParameters;
+
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return SupportsRsa_Pss_Pss(signatureAlgorithm)
+ && publicKey is RsaKeyParameters;
+
+ default:
+ return false;
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void ValidateKeyUsage(int keyUsageBits)
+ {
+ if (!SupportsKeyUsage(keyUsageBits))
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ValidateRsa_Pkcs1()
+ {
+ if (!SupportsRsa_Pkcs1())
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ValidateRsa_Pss_Pss(short signatureAlgorithm)
+ {
+ if (!SupportsRsa_Pss_Pss(signatureAlgorithm))
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ /// <exception cref="IOException"/>
+ protected virtual void ValidateRsa_Pss_Rsae()
+ {
+ if (!SupportsRsa_Pss_Rsae())
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
new file mode 100644
index 000000000..aa9985ed9
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
@@ -0,0 +1,655 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /**
+ * Class for providing cryptographic services for TLS based on implementations in the BC light-weight API.
+ * <p>
+ * This class provides default implementations for everything. If you need to customise it, extend the class
+ * and override the appropriate methods.
+ * </p>
+ */
+ public class BcTlsCrypto
+ : AbstractTlsCrypto
+ {
+ private readonly SecureRandom m_entropySource;
+
+ public BcTlsCrypto(SecureRandom entropySource)
+ {
+ this.m_entropySource = entropySource;
+ }
+
+ internal virtual BcTlsSecret AdoptLocalSecret(byte[] data)
+ {
+ return new BcTlsSecret(this, data);
+ }
+
+ public override SecureRandom SecureRandom
+ {
+ get { return m_entropySource; }
+ }
+
+ public override TlsCertificate CreateCertificate(byte[] encoding)
+ {
+ return new BcTlsCertificate(this, encoding);
+ }
+
+ public override TlsCipher CreateCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm,
+ int macAlgorithm)
+ {
+ switch (encryptionAlgorithm)
+ {
+ case EncryptionAlgorithm.AES_128_CBC:
+ case EncryptionAlgorithm.ARIA_128_CBC:
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ case EncryptionAlgorithm.SEED_CBC:
+ case EncryptionAlgorithm.SM4_CBC:
+ return CreateCipher_Cbc(cryptoParams, encryptionAlgorithm, 16, macAlgorithm);
+
+ case EncryptionAlgorithm.cls_3DES_EDE_CBC:
+ return CreateCipher_Cbc(cryptoParams, encryptionAlgorithm, 24, macAlgorithm);
+
+ case EncryptionAlgorithm.AES_256_CBC:
+ case EncryptionAlgorithm.ARIA_256_CBC:
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ return CreateCipher_Cbc(cryptoParams, encryptionAlgorithm, 32, macAlgorithm);
+
+ case EncryptionAlgorithm.AES_128_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(cryptoParams, 16, 16);
+ case EncryptionAlgorithm.AES_128_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(cryptoParams, 16, 8);
+ case EncryptionAlgorithm.AES_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Gcm(cryptoParams, 16, 16);
+ case EncryptionAlgorithm.AES_256_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(cryptoParams, 32, 16);
+ case EncryptionAlgorithm.AES_256_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(cryptoParams, 32, 8);
+ case EncryptionAlgorithm.AES_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Gcm(cryptoParams, 32, 16);
+ case EncryptionAlgorithm.ARIA_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aria_Gcm(cryptoParams, 16, 16);
+ case EncryptionAlgorithm.ARIA_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aria_Gcm(cryptoParams, 32, 16);
+ case EncryptionAlgorithm.CAMELLIA_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Camellia_Gcm(cryptoParams, 16, 16);
+ case EncryptionAlgorithm.CAMELLIA_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Camellia_Gcm(cryptoParams, 32, 16);
+ case EncryptionAlgorithm.CHACHA20_POLY1305:
+ // NOTE: Ignores macAlgorithm
+ return CreateChaCha20Poly1305(cryptoParams);
+ case EncryptionAlgorithm.NULL:
+ return CreateNullCipher(cryptoParams, macAlgorithm);
+ case EncryptionAlgorithm.SM4_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_SM4_Ccm(cryptoParams);
+ case EncryptionAlgorithm.SM4_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_SM4_Gcm(cryptoParams);
+
+ case EncryptionAlgorithm.DES40_CBC:
+ case EncryptionAlgorithm.DES_CBC:
+ case EncryptionAlgorithm.IDEA_CBC:
+ case EncryptionAlgorithm.RC2_CBC_40:
+ case EncryptionAlgorithm.RC4_128:
+ case EncryptionAlgorithm.RC4_40:
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public override TlsDHDomain CreateDHDomain(TlsDHConfig dhConfig)
+ {
+ return new BcTlsDHDomain(this, dhConfig);
+ }
+
+ public override TlsECDomain CreateECDomain(TlsECConfig ecConfig)
+ {
+ switch (ecConfig.NamedGroup)
+ {
+ case NamedGroup.x25519:
+ return new BcX25519Domain(this);
+ case NamedGroup.x448:
+ return new BcX448Domain(this);
+ default:
+ return new BcTlsECDomain(this, ecConfig);
+ }
+ }
+
+ public override TlsEncryptor CreateEncryptor(TlsCertificate certificate)
+ {
+ BcTlsCertificate bcCert = BcTlsCertificate.Convert(this, certificate);
+ bcCert.ValidateKeyUsage(KeyUsage.KeyEncipherment);
+
+ RsaKeyParameters pubKeyRsa = bcCert.GetPubKeyRsa();
+
+ return new BcTlsRsaEncryptor(this, pubKeyRsa);
+ }
+
+ public override TlsNonceGenerator CreateNonceGenerator(byte[] additionalSeedMaterial)
+ {
+ IDigest digest = CreateDigest(CryptoHashAlgorithm.sha256);
+
+ byte[] seed = new byte[digest.GetDigestSize()];
+ SecureRandom.NextBytes(seed);
+
+ DigestRandomGenerator randomGenerator = new DigestRandomGenerator(digest);
+ randomGenerator.AddSeedMaterial(additionalSeedMaterial);
+ randomGenerator.AddSeedMaterial(seed);
+
+ return new BcTlsNonceGenerator(randomGenerator);
+ }
+
+ public override bool HasAllRawSignatureAlgorithms()
+ {
+ // TODO[RFC 8422] Revisit the need to buffer the handshake for "Intrinsic" hash signatures
+ return !HasSignatureAlgorithm(SignatureAlgorithm.ed25519)
+ && !HasSignatureAlgorithm(SignatureAlgorithm.ed448);
+ }
+
+ public override bool HasDHAgreement()
+ {
+ return true;
+ }
+
+ public override bool HasECDHAgreement()
+ {
+ return true;
+ }
+
+ public override bool HasEncryptionAlgorithm(int encryptionAlgorithm)
+ {
+ switch (encryptionAlgorithm)
+ {
+ case EncryptionAlgorithm.DES40_CBC:
+ case EncryptionAlgorithm.DES_CBC:
+ case EncryptionAlgorithm.IDEA_CBC:
+ case EncryptionAlgorithm.RC2_CBC_40:
+ case EncryptionAlgorithm.RC4_128:
+ case EncryptionAlgorithm.RC4_40:
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
+ public override bool HasCryptoHashAlgorithm(int cryptoHashAlgorithm)
+ {
+ return true;
+ }
+
+ public override bool HasCryptoSignatureAlgorithm(int cryptoSignatureAlgorithm)
+ {
+ switch (cryptoSignatureAlgorithm)
+ {
+ case CryptoSignatureAlgorithm.rsa:
+ case CryptoSignatureAlgorithm.dsa:
+ case CryptoSignatureAlgorithm.ecdsa:
+ case CryptoSignatureAlgorithm.rsa_pss_rsae_sha256:
+ case CryptoSignatureAlgorithm.rsa_pss_rsae_sha384:
+ case CryptoSignatureAlgorithm.rsa_pss_rsae_sha512:
+ case CryptoSignatureAlgorithm.ed25519:
+ case CryptoSignatureAlgorithm.ed448:
+ case CryptoSignatureAlgorithm.rsa_pss_pss_sha256:
+ case CryptoSignatureAlgorithm.rsa_pss_pss_sha384:
+ case CryptoSignatureAlgorithm.rsa_pss_pss_sha512:
+ return true;
+
+ // TODO[draft-smyshlyaev-tls12-gost-suites-10]
+ case CryptoSignatureAlgorithm.gostr34102012_256:
+ case CryptoSignatureAlgorithm.gostr34102012_512:
+
+ // TODO[RFC 8998]
+ case CryptoSignatureAlgorithm.sm2:
+
+ default:
+ return false;
+ }
+ }
+
+ public override bool HasMacAlgorithm(int macAlgorithm)
+ {
+ return true;
+ }
+
+ public override bool HasNamedGroup(int namedGroup)
+ {
+ return NamedGroup.RefersToASpecificGroup(namedGroup);
+ }
+
+ public override bool HasRsaEncryption()
+ {
+ return true;
+ }
+
+ public override bool HasSignatureAlgorithm(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.dsa:
+ case SignatureAlgorithm.ecdsa:
+ case SignatureAlgorithm.ed25519:
+ case SignatureAlgorithm.ed448:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ case SignatureAlgorithm.ecdsa_brainpoolP256r1tls13_sha256:
+ case SignatureAlgorithm.ecdsa_brainpoolP384r1tls13_sha384:
+ case SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512:
+ return true;
+
+ // TODO[draft-smyshlyaev-tls12-gost-suites-10]
+ case SignatureAlgorithm.gostr34102012_256:
+ case SignatureAlgorithm.gostr34102012_512:
+ // TODO[RFC 8998]
+ //case SignatureAlgorithm.sm2:
+ default:
+ return false;
+ }
+ }
+
+ public override bool HasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm)
+ {
+ short signature = sigAndHashAlgorithm.Signature;
+
+ switch (sigAndHashAlgorithm.Hash)
+ {
+ case HashAlgorithm.md5:
+ return SignatureAlgorithm.rsa == signature && HasSignatureAlgorithm(signature);
+ default:
+ return HasSignatureAlgorithm(signature);
+ }
+ }
+
+ public override bool HasSignatureScheme(int signatureScheme)
+ {
+ switch (signatureScheme)
+ {
+ case SignatureScheme.sm2sig_sm3:
+ return false;
+ default:
+ {
+ short signature = SignatureScheme.GetSignatureAlgorithm(signatureScheme);
+
+ switch(SignatureScheme.GetCryptoHashAlgorithm(signatureScheme))
+ {
+ case CryptoHashAlgorithm.md5:
+ return SignatureAlgorithm.rsa == signature && HasSignatureAlgorithm(signature);
+ default:
+ return HasSignatureAlgorithm(signature);
+ }
+ }
+ }
+ }
+
+ public override bool HasSrpAuthentication()
+ {
+ return true;
+ }
+
+ public override TlsSecret CreateSecret(byte[] data)
+ {
+ try
+ {
+ return AdoptLocalSecret(Arrays.Clone(data));
+ }
+ finally
+ {
+ // TODO[tls-ops] Add this after checking all callers
+ //if (data != null)
+ //{
+ // Array.Clear(data, 0, data.Length);
+ //}
+ }
+ }
+
+ public override TlsSecret GenerateRsaPreMasterSecret(ProtocolVersion version)
+ {
+ byte[] data = new byte[48];
+ SecureRandom.NextBytes(data);
+ TlsUtilities.WriteVersion(version, data, 0);
+ return AdoptLocalSecret(data);
+ }
+
+ public virtual IDigest CloneDigest(int cryptoHashAlgorithm, IDigest digest)
+ {
+ switch (cryptoHashAlgorithm)
+ {
+ case CryptoHashAlgorithm.md5:
+ return new MD5Digest((MD5Digest)digest);
+ case CryptoHashAlgorithm.sha1:
+ return new Sha1Digest((Sha1Digest)digest);
+ case CryptoHashAlgorithm.sha224:
+ return new Sha224Digest((Sha224Digest)digest);
+ case CryptoHashAlgorithm.sha256:
+ return new Sha256Digest((Sha256Digest)digest);
+ case CryptoHashAlgorithm.sha384:
+ return new Sha384Digest((Sha384Digest)digest);
+ case CryptoHashAlgorithm.sha512:
+ return new Sha512Digest((Sha512Digest)digest);
+ case CryptoHashAlgorithm.sm3:
+ return new SM3Digest((SM3Digest)digest);
+ default:
+ throw new ArgumentException("invalid CryptoHashAlgorithm: " + cryptoHashAlgorithm);
+ }
+ }
+
+ public virtual IDigest CreateDigest(int cryptoHashAlgorithm)
+ {
+ switch (cryptoHashAlgorithm)
+ {
+ case CryptoHashAlgorithm.md5:
+ return new MD5Digest();
+ case CryptoHashAlgorithm.sha1:
+ return new Sha1Digest();
+ case CryptoHashAlgorithm.sha224:
+ return new Sha224Digest();
+ case CryptoHashAlgorithm.sha256:
+ return new Sha256Digest();
+ case CryptoHashAlgorithm.sha384:
+ return new Sha384Digest();
+ case CryptoHashAlgorithm.sha512:
+ return new Sha512Digest();
+ case CryptoHashAlgorithm.sm3:
+ return new SM3Digest();
+ default:
+ throw new ArgumentException("invalid CryptoHashAlgorithm: " + cryptoHashAlgorithm);
+ }
+ }
+
+ public override TlsHash CreateHash(int cryptoHashAlgorithm)
+ {
+ return new BcTlsHash(this, cryptoHashAlgorithm);
+ }
+
+ protected virtual IBlockCipher CreateBlockCipher(int encryptionAlgorithm)
+ {
+ switch (encryptionAlgorithm)
+ {
+ case EncryptionAlgorithm.cls_3DES_EDE_CBC:
+ return CreateDesEdeEngine();
+ case EncryptionAlgorithm.AES_128_CBC:
+ case EncryptionAlgorithm.AES_256_CBC:
+ return CreateAesEngine();
+ case EncryptionAlgorithm.ARIA_128_CBC:
+ case EncryptionAlgorithm.ARIA_256_CBC:
+ return CreateAriaEngine();
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ return CreateCamelliaEngine();
+ case EncryptionAlgorithm.SEED_CBC:
+ return CreateSeedEngine();
+ case EncryptionAlgorithm.SM4_CBC:
+ return CreateSM4Engine();
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual IBlockCipher CreateCbcBlockCipher(IBlockCipher blockCipher)
+ {
+ return new CbcBlockCipher(blockCipher);
+ }
+
+ protected virtual IBlockCipher CreateCbcBlockCipher(int encryptionAlgorithm)
+ {
+ return CreateCbcBlockCipher(CreateBlockCipher(encryptionAlgorithm));
+ }
+
+ protected virtual TlsCipher CreateChaCha20Poly1305(TlsCryptoParameters cryptoParams)
+ {
+ BcChaCha20Poly1305 encrypt = new BcChaCha20Poly1305(true);
+ BcChaCha20Poly1305 decrypt = new BcChaCha20Poly1305(false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 32, 16, TlsAeadCipher.AEAD_CHACHA20_POLY1305);
+ }
+
+ protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsCryptoParameters cryptoParams, int cipherKeySize,
+ int macSize)
+ {
+ BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Ccm(), true);
+ BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Ccm(), false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_CCM);
+ }
+
+ protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize,
+ int macSize)
+ {
+ BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Gcm(), true);
+ BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Gcm(), false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM);
+ }
+
+ protected virtual TlsAeadCipher CreateCipher_Aria_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize,
+ int macSize)
+ {
+ BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aria_Gcm(), true);
+ BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aria_Gcm(), false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM);
+ }
+
+ protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize,
+ int macSize)
+ {
+ BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Camellia_Gcm(), true);
+ BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Camellia_Gcm(), false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM);
+ }
+
+ protected virtual TlsCipher CreateCipher_Cbc(TlsCryptoParameters cryptoParams, int encryptionAlgorithm,
+ int cipherKeySize, int macAlgorithm)
+ {
+ BcTlsBlockCipherImpl encrypt = new BcTlsBlockCipherImpl(CreateCbcBlockCipher(encryptionAlgorithm), true);
+ BcTlsBlockCipherImpl decrypt = new BcTlsBlockCipherImpl(CreateCbcBlockCipher(encryptionAlgorithm), false);
+
+ TlsHmac clientMac = CreateMac(cryptoParams, macAlgorithm);
+ TlsHmac serverMac = CreateMac(cryptoParams, macAlgorithm);
+
+ return new TlsBlockCipher(cryptoParams, encrypt, decrypt, clientMac, serverMac, cipherKeySize);
+ }
+
+ protected virtual TlsAeadCipher CreateCipher_SM4_Ccm(TlsCryptoParameters cryptoParams)
+ {
+ BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Ccm(), true);
+ BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Ccm(), false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_CCM);
+ }
+
+ protected virtual TlsAeadCipher CreateCipher_SM4_Gcm(TlsCryptoParameters cryptoParams)
+ {
+ BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Gcm(), true);
+ BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Gcm(), false);
+
+ return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_GCM);
+ }
+
+ protected virtual TlsNullCipher CreateNullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
+ {
+ return new TlsNullCipher(cryptoParams, CreateMac(cryptoParams, macAlgorithm),
+ CreateMac(cryptoParams, macAlgorithm));
+ }
+
+ protected virtual IBlockCipher CreateAesEngine()
+ {
+ return new AesEngine();
+ }
+
+ protected virtual IBlockCipher CreateAriaEngine()
+ {
+ return new AriaEngine();
+ }
+
+ protected virtual IBlockCipher CreateCamelliaEngine()
+ {
+ return new CamelliaEngine();
+ }
+
+ protected virtual IBlockCipher CreateDesEdeEngine()
+ {
+ return new DesEdeEngine();
+ }
+
+ protected virtual IBlockCipher CreateSeedEngine()
+ {
+ return new SeedEngine();
+ }
+
+ protected virtual IBlockCipher CreateSM4Engine()
+ {
+ return new SM4Engine();
+ }
+
+ protected virtual IAeadBlockCipher CreateCcmMode(IBlockCipher engine)
+ {
+ return new CcmBlockCipher(engine);
+ }
+
+ protected virtual IAeadBlockCipher CreateGcmMode(IBlockCipher engine)
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GcmBlockCipher(engine);
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm()
+ {
+ return CreateCcmMode(CreateAesEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm()
+ {
+ return CreateGcmMode(CreateAesEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aria_Gcm()
+ {
+ return CreateGcmMode(CreateAriaEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm()
+ {
+ return CreateGcmMode(CreateCamelliaEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_SM4_Ccm()
+ {
+ return CreateCcmMode(CreateSM4Engine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_SM4_Gcm()
+ {
+ return CreateGcmMode(CreateSM4Engine());
+ }
+
+ public override TlsHmac CreateHmac(int macAlgorithm)
+ {
+ return CreateHmacForHash(TlsCryptoUtilities.GetHashForHmac(macAlgorithm));
+ }
+
+ public override TlsHmac CreateHmacForHash(int cryptoHashAlgorithm)
+ {
+ return new BcTlsHmac(new HMac(CreateDigest(cryptoHashAlgorithm)));
+ }
+
+ protected virtual TlsHmac CreateHmac_Ssl(int macAlgorithm)
+ {
+ switch (macAlgorithm)
+ {
+ case MacAlgorithm.hmac_md5:
+ return new BcSsl3Hmac(CreateDigest(CryptoHashAlgorithm.md5));
+ case MacAlgorithm.hmac_sha1:
+ return new BcSsl3Hmac(CreateDigest(CryptoHashAlgorithm.sha1));
+ case MacAlgorithm.hmac_sha256:
+ return new BcSsl3Hmac(CreateDigest(CryptoHashAlgorithm.sha256));
+ case MacAlgorithm.hmac_sha384:
+ return new BcSsl3Hmac(CreateDigest(CryptoHashAlgorithm.sha384));
+ case MacAlgorithm.hmac_sha512:
+ return new BcSsl3Hmac(CreateDigest(CryptoHashAlgorithm.sha512));
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual TlsHmac CreateMac(TlsCryptoParameters cryptoParams, int macAlgorithm)
+ {
+ if (TlsImplUtilities.IsSsl(cryptoParams))
+ {
+ return CreateHmac_Ssl(macAlgorithm);
+ }
+ else
+ {
+ return CreateHmac(macAlgorithm);
+ }
+ }
+
+ public override TlsSrp6Client CreateSrp6Client(TlsSrpConfig srpConfig)
+ {
+ BigInteger[] ng = srpConfig.GetExplicitNG();
+ Srp6GroupParameters srpGroup = new Srp6GroupParameters(ng[0], ng[1]);
+
+ Srp6Client srp6Client = new Srp6Client();
+ srp6Client.Init(srpGroup, CreateDigest(CryptoHashAlgorithm.sha1), SecureRandom);
+
+ return new BcTlsSrp6Client(srp6Client);
+ }
+
+ public override TlsSrp6Server CreateSrp6Server(TlsSrpConfig srpConfig, BigInteger srpVerifier)
+ {
+ BigInteger[] ng = srpConfig.GetExplicitNG();
+ Srp6GroupParameters srpGroup = new Srp6GroupParameters(ng[0], ng[1]);
+
+ Srp6Server srp6Server = new Srp6Server();
+ srp6Server.Init(srpGroup, srpVerifier, CreateDigest(CryptoHashAlgorithm.sha1), SecureRandom);
+
+ return new BcTlsSrp6Server(srp6Server);
+ }
+
+ public override TlsSrp6VerifierGenerator CreateSrp6VerifierGenerator(TlsSrpConfig srpConfig)
+ {
+ BigInteger[] ng = srpConfig.GetExplicitNG();
+
+ Srp6VerifierGenerator srp6VerifierGenerator = new Srp6VerifierGenerator();
+ srp6VerifierGenerator.Init(ng[0], ng[1], CreateDigest(CryptoHashAlgorithm.sha1));
+
+ return new BcTlsSrp6VerifierGenerator(srp6VerifierGenerator);
+ }
+
+ public override TlsSecret HkdfInit(int cryptoHashAlgorithm)
+ {
+ return AdoptLocalSecret(new byte[TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm)]);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsDH.cs b/crypto/src/tls/crypto/impl/bc/BcTlsDH.cs
new file mode 100644
index 000000000..8af94f7c6
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsDH.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Support class for ephemeral Diffie-Hellman using the BC light-weight library.</summary>
+ public class BcTlsDH
+ : TlsAgreement
+ {
+ protected readonly BcTlsDHDomain m_domain;
+
+ protected AsymmetricCipherKeyPair m_localKeyPair;
+ protected DHPublicKeyParameters m_peerPublicKey;
+
+ public BcTlsDH(BcTlsDHDomain domain)
+ {
+ this.m_domain = domain;
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual byte[] GenerateEphemeral()
+ {
+ this.m_localKeyPair = m_domain.GenerateKeyPair();
+
+ return m_domain.EncodePublicKey((DHPublicKeyParameters)m_localKeyPair.Public);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual void ReceivePeerValue(byte[] peerValue)
+ {
+ this.m_peerPublicKey = m_domain.DecodePublicKey(peerValue);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual TlsSecret CalculateSecret()
+ {
+ return m_domain.CalculateDHAgreement((DHPrivateKeyParameters)m_localKeyPair.Private, m_peerPublicKey);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsDHDomain.cs b/crypto/src/tls/crypto/impl/bc/BcTlsDHDomain.cs
new file mode 100644
index 000000000..90b8ce94f
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsDHDomain.cs
@@ -0,0 +1,119 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>BC light-weight support class for Diffie-Hellman key pair generation and key agreement over a
+ /// specified Diffie-Hellman configuration.</summary>
+ public class BcTlsDHDomain
+ : TlsDHDomain
+ {
+ private static byte[] EncodeValue(DHParameters dh, bool padded, BigInteger x)
+ {
+ return padded
+ ? BigIntegers.AsUnsignedByteArray(GetValueLength(dh), x)
+ : BigIntegers.AsUnsignedByteArray(x);
+ }
+
+ private static int GetValueLength(DHParameters dh)
+ {
+ return (dh.P.BitLength + 7) / 8;
+ }
+
+ public static BcTlsSecret CalculateDHAgreement(BcTlsCrypto crypto, DHPrivateKeyParameters privateKey,
+ DHPublicKeyParameters publicKey, bool padded)
+ {
+ DHBasicAgreement basicAgreement = new DHBasicAgreement();
+ basicAgreement.Init(privateKey);
+ BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey);
+ byte[] secret = EncodeValue(privateKey.Parameters, padded, agreementValue);
+ return crypto.AdoptLocalSecret(secret);
+ }
+
+ public static DHParameters GetParameters(TlsDHConfig dhConfig)
+ {
+ DHGroup dhGroup = TlsDHUtilities.GetDHGroup(dhConfig);
+ if (dhGroup == null)
+ throw new ArgumentException("No DH configuration provided");
+
+ return new DHParameters(dhGroup.P, dhGroup.G, dhGroup.Q, dhGroup.L);
+ }
+
+ protected readonly BcTlsCrypto crypto;
+ protected readonly TlsDHConfig dhConfig;
+ protected readonly DHParameters dhParameters;
+
+ public BcTlsDHDomain(BcTlsCrypto crypto, TlsDHConfig dhConfig)
+ {
+ this.crypto = crypto;
+ this.dhConfig = dhConfig;
+ this.dhParameters = GetParameters(dhConfig);
+ }
+
+ public virtual BcTlsSecret CalculateDHAgreement(DHPrivateKeyParameters privateKey,
+ DHPublicKeyParameters publicKey)
+ {
+ return CalculateDHAgreement(crypto, privateKey, publicKey, dhConfig.IsPadded);
+ }
+
+ public virtual TlsAgreement CreateDH()
+ {
+ return new BcTlsDH(this);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual BigInteger DecodeParameter(byte[] encoding)
+ {
+ if (dhConfig.IsPadded && GetValueLength(dhParameters) != encoding.Length)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return new BigInteger(1, encoding);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual DHPublicKeyParameters DecodePublicKey(byte[] encoding)
+ {
+ /*
+ * RFC 7919 3. [..] the client MUST verify that dh_Ys is in the range 1 < dh_Ys < dh_p - 1.
+ * If dh_Ys is not in this range, the client MUST terminate the connection with a fatal
+ * handshake_failure(40) alert.
+ */
+ try
+ {
+ BigInteger y = DecodeParameter(encoding);
+
+ return new DHPublicKeyParameters(y, dhParameters);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure, e);
+ }
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual byte[] EncodeParameter(BigInteger x)
+ {
+ return EncodeValue(dhParameters, dhConfig.IsPadded, x);
+ }
+
+ /// <exception cref="IOException"/>
+ public virtual byte[] EncodePublicKey(DHPublicKeyParameters publicKey)
+ {
+ return EncodeValue(dhParameters, true, publicKey.Y);
+ }
+
+ public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ DHBasicKeyPairGenerator keyPairGenerator = new DHBasicKeyPairGenerator();
+ keyPairGenerator.Init(new DHKeyGenerationParameters(crypto.SecureRandom, dhParameters));
+ return keyPairGenerator.GenerateKeyPair();
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsDsaSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsDsaSigner.cs
new file mode 100644
index 000000000..a1040284c
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsDsaSigner.cs
@@ -0,0 +1,29 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for generation of the raw DSA signature type using the BC light-weight API.
+ /// </summary>
+ public class BcTlsDsaSigner
+ : BcTlsDssSigner
+ {
+ public BcTlsDsaSigner(BcTlsCrypto crypto, DsaPrivateKeyParameters privateKey)
+ : base(crypto, privateKey)
+ {
+ }
+
+ protected override IDsa CreateDsaImpl(int cryptoHashAlgorithm)
+ {
+ return new DsaSigner(new HMacDsaKCalculator(m_crypto.CreateDigest(cryptoHashAlgorithm)));
+ }
+
+ protected override short SignatureAlgorithm
+ {
+ get { return Tls.SignatureAlgorithm.dsa; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsDsaVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsDsaVerifier.cs
new file mode 100644
index 000000000..bc8395ac5
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsDsaVerifier.cs
@@ -0,0 +1,29 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for the verification of the raw DSA signature type using the BC light-weight API.
+ /// </summary>
+ public class BcTlsDsaVerifier
+ : BcTlsDssVerifier
+ {
+ public BcTlsDsaVerifier(BcTlsCrypto crypto, DsaPublicKeyParameters publicKey)
+ : base(crypto, publicKey)
+ {
+ }
+
+ protected override IDsa CreateDsaImpl(int cryptoHashAlgorithm)
+ {
+ return new DsaSigner(new HMacDsaKCalculator(m_crypto.CreateDigest(cryptoHashAlgorithm)));
+ }
+
+ protected override short SignatureAlgorithm
+ {
+ get { return Tls.SignatureAlgorithm.dsa; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsDssSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsDssSigner.cs
new file mode 100644
index 000000000..8a687d56c
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsDssSigner.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>BC light-weight base class for the signers implementing the two DSA style algorithms from FIPS PUB
+ /// 186-4: DSA and ECDSA.</summary>
+ public abstract class BcTlsDssSigner
+ : BcTlsSigner
+ {
+ protected BcTlsDssSigner(BcTlsCrypto crypto, AsymmetricKeyParameter privateKey)
+ : base(crypto, privateKey)
+ {
+ }
+
+ protected abstract IDsa CreateDsaImpl(int cryptoHashAlgorithm);
+
+ protected abstract short SignatureAlgorithm { get; }
+
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash)
+ {
+ if (algorithm != null && algorithm.Signature != SignatureAlgorithm)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ int cryptoHashAlgorithm = (null == algorithm)
+ ? CryptoHashAlgorithm.sha1
+ : TlsCryptoUtilities.GetHash(algorithm.Hash);
+
+ ISigner signer = new DsaDigestSigner(CreateDsaImpl(cryptoHashAlgorithm), new NullDigest());
+ signer.Init(true, new ParametersWithRandom(m_privateKey, m_crypto.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);
+ }
+ try
+ {
+ return signer.GenerateSignature();
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsDssVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsDssVerifier.cs
new file mode 100644
index 000000000..f2e371067
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsDssVerifier.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>BC light-weight base class for the verifiers supporting the two DSA style algorithms from FIPS PUB
+ /// 186-4: DSA and ECDSA.</summary>
+ public abstract class BcTlsDssVerifier
+ : BcTlsVerifier
+ {
+ protected BcTlsDssVerifier(BcTlsCrypto crypto, AsymmetricKeyParameter publicKey)
+ : base(crypto, publicKey)
+ {
+ }
+
+ protected abstract IDsa CreateDsaImpl(int cryptoHashAlgorithm);
+
+ protected abstract short SignatureAlgorithm { get; }
+
+ public override bool VerifyRawSignature(DigitallySigned signedParams, byte[] hash)
+ {
+ SignatureAndHashAlgorithm algorithm = signedParams.Algorithm;
+ if (algorithm != null && algorithm.Signature != SignatureAlgorithm)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ int cryptoHashAlgorithm = (null == algorithm)
+ ? CryptoHashAlgorithm.sha1
+ : TlsCryptoUtilities.GetHash(algorithm.Hash);
+
+ ISigner signer = new DsaDigestSigner(CreateDsaImpl(cryptoHashAlgorithm), new NullDigest());
+ signer.Init(false, m_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(signedParams.Signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsECDH.cs b/crypto/src/tls/crypto/impl/bc/BcTlsECDH.cs
new file mode 100644
index 000000000..55b8ed60a
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsECDH.cs
@@ -0,0 +1,39 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Support class for ephemeral Elliptic Curve Diffie-Hellman using the BC light-weight library.</summary>
+ public class BcTlsECDH
+ : TlsAgreement
+ {
+ protected readonly BcTlsECDomain m_domain;
+
+ protected AsymmetricCipherKeyPair m_localKeyPair;
+ protected ECPublicKeyParameters m_peerPublicKey;
+
+ public BcTlsECDH(BcTlsECDomain domain)
+ {
+ this.m_domain = domain;
+ }
+
+ public virtual byte[] GenerateEphemeral()
+ {
+ this.m_localKeyPair = m_domain.GenerateKeyPair();
+
+ return m_domain.EncodePublicKey((ECPublicKeyParameters)m_localKeyPair.Public);
+ }
+
+ public virtual void ReceivePeerValue(byte[] peerValue)
+ {
+ this.m_peerPublicKey = m_domain.DecodePublicKey(peerValue);
+ }
+
+ public virtual TlsSecret CalculateSecret()
+ {
+ return m_domain.CalculateECDHAgreement((ECPrivateKeyParameters)m_localKeyPair.Private, m_peerPublicKey);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsECDomain.cs b/crypto/src/tls/crypto/impl/bc/BcTlsECDomain.cs
new file mode 100644
index 000000000..f4c5cfc5d
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsECDomain.cs
@@ -0,0 +1,125 @@
+using System;
+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.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /**
+ * EC domain class for generating key pairs and performing key agreement.
+ */
+ public class BcTlsECDomain
+ : TlsECDomain
+ {
+ public static BcTlsSecret CalculateBasicAgreement(BcTlsCrypto crypto, ECPrivateKeyParameters privateKey,
+ ECPublicKeyParameters publicKey)
+ {
+ 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.
+ */
+ byte[] secret = BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
+ return crypto.AdoptLocalSecret(secret);
+ }
+
+ public static ECDomainParameters GetDomainParameters(TlsECConfig ecConfig)
+ {
+ return GetDomainParameters(ecConfig.NamedGroup);
+ }
+
+ public static ECDomainParameters GetDomainParameters(int namedGroup)
+ {
+ if (!NamedGroup.RefersToASpecificCurve(namedGroup))
+ return null;
+
+ // Parameters are lazily created the first time a particular curve is accessed
+
+ string curveName = NamedGroup.GetCurveName(namedGroup);
+ 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());
+ }
+
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly TlsECConfig m_ecConfig;
+ protected readonly ECDomainParameters m_ecDomainParameters;
+
+ public BcTlsECDomain(BcTlsCrypto crypto, TlsECConfig ecConfig)
+ {
+ this.m_crypto = crypto;
+ this.m_ecConfig = ecConfig;
+ this.m_ecDomainParameters = GetDomainParameters(ecConfig);
+ }
+
+ public virtual BcTlsSecret CalculateECDHAgreement(ECPrivateKeyParameters privateKey,
+ ECPublicKeyParameters publicKey)
+ {
+ return CalculateBasicAgreement(m_crypto, privateKey, publicKey);
+ }
+
+ public virtual TlsAgreement CreateECDH()
+ {
+ return new BcTlsECDH(this);
+ }
+
+ public virtual ECPoint DecodePoint(byte[] encoding)
+ {
+ return m_ecDomainParameters.Curve.DecodePoint(encoding);
+ }
+
+ public virtual ECPublicKeyParameters DecodePublicKey(byte[] encoding)
+ {
+ try
+ {
+ ECPoint point = DecodePoint(encoding);
+
+ return new ECPublicKeyParameters(point, m_ecDomainParameters);
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
+ }
+ }
+
+ public virtual byte[] EncodePoint(ECPoint point)
+ {
+ return point.GetEncoded(false);
+ }
+
+ public virtual byte[] EncodePublicKey(ECPublicKeyParameters publicKey)
+ {
+ return EncodePoint(publicKey.Q);
+ }
+
+ public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+ keyPairGenerator.Init(new ECKeyGenerationParameters(m_ecDomainParameters, m_crypto.SecureRandom));
+ return keyPairGenerator.GenerateKeyPair();
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsECDsa13Signer.cs b/crypto/src/tls/crypto/impl/bc/BcTlsECDsa13Signer.cs
new file mode 100644
index 000000000..590176772
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsECDsa13Signer.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for generation of ECDSA signatures in TLS 1.3+ using the BC light-weight API.
+ /// </summary>
+ public class BcTlsECDsa13Signer
+ : BcTlsSigner
+ {
+ private readonly int m_signatureScheme;
+
+ public BcTlsECDsa13Signer(BcTlsCrypto crypto, ECPrivateKeyParameters privateKey, int signatureScheme)
+ : base(crypto, privateKey)
+ {
+ if (!SignatureScheme.IsECDsa(signatureScheme))
+ throw new ArgumentException("signatureScheme");
+
+ this.m_signatureScheme = signatureScheme;
+ }
+
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash)
+ {
+ if (algorithm == null || SignatureScheme.From(algorithm) != m_signatureScheme)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(m_signatureScheme);
+ IDsa dsa = new ECDsaSigner(new HMacDsaKCalculator(m_crypto.CreateDigest(cryptoHashAlgorithm)));
+
+ ISigner signer = new DsaDigestSigner(dsa, new NullDigest());
+ signer.Init(true, new ParametersWithRandom(m_privateKey, m_crypto.SecureRandom));
+ signer.BlockUpdate(hash, 0, hash.Length);
+ try
+ {
+ return signer.GenerateSignature();
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsECDsa13Verifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsECDsa13Verifier.cs
new file mode 100644
index 000000000..8488dc8d2
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsECDsa13Verifier.cs
@@ -0,0 +1,41 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for verification of ECDSA signatures in TLS 1.3+ using the BC light-weight API.
+ /// </summary>
+ public class BcTlsECDsa13Verifier
+ : BcTlsVerifier
+ {
+ private readonly int m_signatureScheme;
+
+ public BcTlsECDsa13Verifier(BcTlsCrypto crypto, ECPublicKeyParameters publicKey, int signatureScheme)
+ : base(crypto, publicKey)
+ {
+ if (!SignatureScheme.IsECDsa(signatureScheme))
+ throw new ArgumentException("signatureScheme");
+
+ this.m_signatureScheme = signatureScheme;
+ }
+
+ public override bool VerifyRawSignature(DigitallySigned signature, byte[] hash)
+ {
+ SignatureAndHashAlgorithm algorithm = signature.Algorithm;
+ if (algorithm == null || SignatureScheme.From(algorithm) != m_signatureScheme)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(m_signatureScheme);
+ IDsa dsa = new ECDsaSigner(new HMacDsaKCalculator(m_crypto.CreateDigest(cryptoHashAlgorithm)));
+
+ ISigner signer = new DsaDigestSigner(dsa, new NullDigest());
+ signer.Init(false, m_publicKey);
+ signer.BlockUpdate(hash, 0, hash.Length);
+ return signer.VerifySignature(signature.Signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsECDsaSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsECDsaSigner.cs
new file mode 100644
index 000000000..8b29912af
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsECDsaSigner.cs
@@ -0,0 +1,29 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for generation of the raw ECDSA signature type using the BC light-weight API.
+ /// </summary>
+ public class BcTlsECDsaSigner
+ : BcTlsDssSigner
+ {
+ public BcTlsECDsaSigner(BcTlsCrypto crypto, ECPrivateKeyParameters privateKey)
+ : base(crypto, privateKey)
+ {
+ }
+
+ protected override IDsa CreateDsaImpl(int cryptoHashAlgorithm)
+ {
+ return new ECDsaSigner(new HMacDsaKCalculator(m_crypto.CreateDigest(cryptoHashAlgorithm)));
+ }
+
+ protected override short SignatureAlgorithm
+ {
+ get { return Tls.SignatureAlgorithm.ecdsa; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsECDsaVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsECDsaVerifier.cs
new file mode 100644
index 000000000..1cd6ff1ea
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsECDsaVerifier.cs
@@ -0,0 +1,29 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Implementation class for the verification of the raw ECDSA signature type using the BC light-weight
+ /// API.</summary>
+ public class BcTlsECDsaVerifier
+ : BcTlsDssVerifier
+ {
+ public BcTlsECDsaVerifier(BcTlsCrypto crypto, ECPublicKeyParameters publicKey)
+ : base(crypto, publicKey)
+ {
+ }
+
+ protected override IDsa CreateDsaImpl(int cryptoHashAlgorithm)
+ {
+ return new ECDsaSigner(new HMacDsaKCalculator(m_crypto.CreateDigest(cryptoHashAlgorithm)));
+ }
+
+ protected override short SignatureAlgorithm
+ {
+ get { return Tls.SignatureAlgorithm.ecdsa; }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsEd25519Signer.cs b/crypto/src/tls/crypto/impl/bc/BcTlsEd25519Signer.cs
new file mode 100644
index 000000000..c8fc99e18
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsEd25519Signer.cs
@@ -0,0 +1,32 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public class BcTlsEd25519Signer
+ : BcTlsSigner
+ {
+ public BcTlsEd25519Signer(BcTlsCrypto crypto, Ed25519PrivateKeyParameters privateKey)
+ : base(crypto, privateKey)
+ {
+ }
+
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override TlsStreamSigner GetStreamSigner(SignatureAndHashAlgorithm algorithm)
+ {
+ if (algorithm == null || SignatureScheme.From(algorithm) != SignatureScheme.ed25519)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ Ed25519Signer signer = new Ed25519Signer();
+ signer.Init(true, m_privateKey);
+
+ return new BcTlsStreamSigner(signer);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsEd25519Verifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsEd25519Verifier.cs
new file mode 100644
index 000000000..fb91442d9
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsEd25519Verifier.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public class BcTlsEd25519Verifier
+ : BcTlsVerifier
+ {
+ public BcTlsEd25519Verifier(BcTlsCrypto crypto, Ed25519PublicKeyParameters publicKey)
+ : base(crypto, publicKey)
+ {
+ }
+
+ public override bool VerifyRawSignature(DigitallySigned signature, byte[] hash)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override TlsStreamVerifier GetStreamVerifier(DigitallySigned signature)
+ {
+ SignatureAndHashAlgorithm algorithm = signature.Algorithm;
+ if (algorithm == null || SignatureScheme.From(algorithm) != SignatureScheme.ed25519)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ Ed25519Signer verifier = new Ed25519Signer();
+ verifier.Init(false, m_publicKey);
+
+ return new BcTlsStreamVerifier(verifier, signature.Signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsEd448Signer.cs b/crypto/src/tls/crypto/impl/bc/BcTlsEd448Signer.cs
new file mode 100644
index 000000000..06d4d7ad9
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsEd448Signer.cs
@@ -0,0 +1,32 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public class BcTlsEd448Signer
+ : BcTlsSigner
+ {
+ public BcTlsEd448Signer(BcTlsCrypto crypto, Ed448PrivateKeyParameters privateKey)
+ : base(crypto, privateKey)
+ {
+ }
+
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override TlsStreamSigner GetStreamSigner(SignatureAndHashAlgorithm algorithm)
+ {
+ if (algorithm == null || SignatureScheme.From(algorithm) != SignatureScheme.ed448)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ Ed448Signer signer = new Ed448Signer(TlsUtilities.EmptyBytes);
+ signer.Init(true, m_privateKey);
+
+ return new BcTlsStreamSigner(signer);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsEd448Verifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsEd448Verifier.cs
new file mode 100644
index 000000000..4492fdd60
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsEd448Verifier.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public class BcTlsEd448Verifier
+ : BcTlsVerifier
+ {
+ public BcTlsEd448Verifier(BcTlsCrypto crypto, Ed448PublicKeyParameters publicKey)
+ : base(crypto, publicKey)
+ {
+ }
+
+ public override bool VerifyRawSignature(DigitallySigned signature, byte[] hash)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override TlsStreamVerifier GetStreamVerifier(DigitallySigned signature)
+ {
+ SignatureAndHashAlgorithm algorithm = signature.Algorithm;
+ if (algorithm == null || SignatureScheme.From(algorithm) != SignatureScheme.ed448)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ Ed448Signer verifier = new Ed448Signer(TlsUtilities.EmptyBytes);
+ verifier.Init(false, m_publicKey);
+
+ return new BcTlsStreamVerifier(verifier, signature.Signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
new file mode 100644
index 000000000..0b35831f3
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
@@ -0,0 +1,49 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsHash
+ : TlsHash
+ {
+ private readonly BcTlsCrypto m_crypto;
+ private readonly int m_cryptoHashAlgorithm;
+ private readonly IDigest m_digest;
+
+ internal BcTlsHash(BcTlsCrypto crypto, int cryptoHashAlgorithm)
+ : this(crypto, cryptoHashAlgorithm, crypto.CreateDigest(cryptoHashAlgorithm))
+ {
+ }
+
+ private BcTlsHash(BcTlsCrypto crypto, int cryptoHashAlgorithm, IDigest digest)
+ {
+ this.m_crypto = crypto;
+ this.m_cryptoHashAlgorithm = cryptoHashAlgorithm;
+ this.m_digest = digest;
+ }
+
+ public void Update(byte[] data, int offSet, int length)
+ {
+ m_digest.BlockUpdate(data, offSet, length);
+ }
+
+ public byte[] CalculateHash()
+ {
+ byte[] rv = new byte[m_digest.GetDigestSize()];
+ m_digest.DoFinal(rv, 0);
+ return rv;
+ }
+
+ public TlsHash CloneHash()
+ {
+ IDigest clone = m_crypto.CloneDigest(m_cryptoHashAlgorithm, m_digest);
+ return new BcTlsHash(m_crypto, m_cryptoHashAlgorithm, clone);
+ }
+
+ public void Reset()
+ {
+ m_digest.Reset();
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
new file mode 100644
index 000000000..485a3f744
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
@@ -0,0 +1,55 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsHmac
+ : TlsHmac
+ {
+ private readonly HMac m_hmac;
+
+ internal BcTlsHmac(HMac hmac)
+ {
+ this.m_hmac = hmac;
+ }
+
+ public void SetKey(byte[] key, int keyOff, int keyLen)
+ {
+ m_hmac.Init(new KeyParameter(key, keyOff, keyLen));
+ }
+
+ public void Update(byte[] input, int inOff, int length)
+ {
+ m_hmac.BlockUpdate(input, inOff, length);
+ }
+
+ public byte[] CalculateMac()
+ {
+ byte[] rv = new byte[m_hmac.GetMacSize()];
+ m_hmac.DoFinal(rv, 0);
+ return rv;
+ }
+
+ public void CalculateMac(byte[] output, int outOff)
+ {
+ m_hmac.DoFinal(output, outOff);
+ }
+
+ public int InternalBlockSize
+ {
+ get { return m_hmac.GetUnderlyingDigest().GetByteLength(); }
+ }
+
+ public int MacLength
+ {
+ get { return m_hmac.GetMacSize(); }
+ }
+
+ public void Reset()
+ {
+ m_hmac.Reset();
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsNonceGenerator.cs b/crypto/src/tls/crypto/impl/bc/BcTlsNonceGenerator.cs
new file mode 100644
index 000000000..d33c622a6
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsNonceGenerator.cs
@@ -0,0 +1,24 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Prng;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsNonceGenerator
+ : TlsNonceGenerator
+ {
+ private readonly IRandomGenerator m_randomGenerator;
+
+ internal BcTlsNonceGenerator(IRandomGenerator randomGenerator)
+ {
+ this.m_randomGenerator = randomGenerator;
+ }
+
+ public byte[] GenerateNonce(int size)
+ {
+ byte[] nonce = new byte[size];
+ m_randomGenerator.NextBytes(nonce);
+ return nonce;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsRsaEncryptor.cs b/crypto/src/tls/crypto/impl/bc/BcTlsRsaEncryptor.cs
new file mode 100644
index 000000000..1582ff638
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsRsaEncryptor.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsRsaEncryptor
+ : TlsEncryptor
+ {
+ private static RsaKeyParameters CheckPublicKey(RsaKeyParameters pubKeyRsa)
+ {
+ if (null == pubKeyRsa || pubKeyRsa.IsPrivate)
+ throw new ArgumentException("No public RSA key provided", "pubKeyRsa");
+
+ return pubKeyRsa;
+ }
+
+ private readonly BcTlsCrypto m_crypto;
+ private readonly RsaKeyParameters m_pubKeyRsa;
+
+ internal BcTlsRsaEncryptor(BcTlsCrypto crypto, RsaKeyParameters pubKeyRsa)
+ {
+ this.m_crypto = crypto;
+ this.m_pubKeyRsa = CheckPublicKey(pubKeyRsa);
+ }
+
+ public byte[] Encrypt(byte[] input, int inOff, int length)
+ {
+ try
+ {
+ Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());
+ encoding.Init(true, new ParametersWithRandom(m_pubKeyRsa, m_crypto.SecureRandom));
+ return encoding.ProcessBlock(input, inOff, length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ /*
+ * This should never happen, only during decryption.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsRsaPssSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsRsaPssSigner.cs
new file mode 100644
index 000000000..2c1b41120
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsRsaPssSigner.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Operator supporting the generation of RSASSA-PSS signatures using the BC light-weight API.</summary>
+ public class BcTlsRsaPssSigner
+ : BcTlsSigner
+ {
+ private readonly int m_signatureScheme;
+
+ public BcTlsRsaPssSigner(BcTlsCrypto crypto, RsaKeyParameters privateKey, int signatureScheme)
+ : base(crypto, privateKey)
+ {
+ if (!SignatureScheme.IsRsaPss(signatureScheme))
+ throw new ArgumentException("signatureScheme");
+
+ this.m_signatureScheme = signatureScheme;
+ }
+
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override TlsStreamSigner GetStreamSigner(SignatureAndHashAlgorithm algorithm)
+ {
+ if (algorithm == null || SignatureScheme.From(algorithm) != m_signatureScheme)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(m_signatureScheme);
+ IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
+
+ PssSigner signer = new PssSigner(new RsaBlindedEngine(), digest, digest.GetDigestSize());
+ signer.Init(true, new ParametersWithRandom(m_privateKey, m_crypto.SecureRandom));
+
+ return new BcTlsStreamSigner(signer);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsRsaPssVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsRsaPssVerifier.cs
new file mode 100644
index 000000000..1b14d1ab4
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsRsaPssVerifier.cs
@@ -0,0 +1,45 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Operator supporting the verification of RSASSA-PSS signatures using the BC light-weight API.</summary>
+ public class BcTlsRsaPssVerifier
+ : BcTlsVerifier
+ {
+ private readonly int m_signatureScheme;
+
+ public BcTlsRsaPssVerifier(BcTlsCrypto crypto, RsaKeyParameters publicKey, int signatureScheme)
+ : base(crypto, publicKey)
+ {
+ if (!SignatureScheme.IsRsaPss(signatureScheme))
+ throw new ArgumentException("signatureScheme");
+
+ this.m_signatureScheme = signatureScheme;
+ }
+
+ public override bool VerifyRawSignature(DigitallySigned signature, byte[] hash)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override TlsStreamVerifier GetStreamVerifier(DigitallySigned signature)
+ {
+ SignatureAndHashAlgorithm algorithm = signature.Algorithm;
+ if (algorithm == null || SignatureScheme.From(algorithm) != m_signatureScheme)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(m_signatureScheme);
+ IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
+
+ PssSigner verifier = new PssSigner(new RsaEngine(), digest, digest.GetDigestSize());
+ verifier.Init(false, m_publicKey);
+
+ return new BcTlsStreamVerifier(verifier, signature.Signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsRsaSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsRsaSigner.cs
new file mode 100644
index 000000000..0e741a57a
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsRsaSigner.cs
@@ -0,0 +1,71 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Operator supporting the generation of RSASSA-PKCS1-v1_5 signatures using the BC light-weight API.
+ /// </summary>
+ public class BcTlsRsaSigner
+ : BcTlsSigner
+ {
+ private readonly RsaKeyParameters m_publicKey;
+
+ public BcTlsRsaSigner(BcTlsCrypto crypto, RsaKeyParameters privateKey, RsaKeyParameters publicKey)
+ : base(crypto, privateKey)
+ {
+ this.m_publicKey = publicKey;
+ }
+
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash)
+ {
+ IDigest nullDigest = new NullDigest();
+
+ ISigner signer;
+ if (algorithm != null)
+ {
+ if (algorithm.Signature != SignatureAlgorithm.rsa)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ /*
+ * 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].
+ */
+ signer = new RsaDigestSigner(nullDigest, 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.
+ */
+ signer = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), nullDigest);
+ }
+ signer.Init(true, new ParametersWithRandom(m_privateKey, m_crypto.SecureRandom));
+ signer.BlockUpdate(hash, 0, hash.Length);
+ try
+ {
+ byte[] signature = signer.GenerateSignature();
+
+ signer.Init(false, m_publicKey);
+ signer.BlockUpdate(hash, 0, hash.Length);
+
+ if (signer.VerifySignature(signature))
+ {
+ return signature;
+ }
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsRsaVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsRsaVerifier.cs
new file mode 100644
index 000000000..41f1bb1be
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsRsaVerifier.cs
@@ -0,0 +1,52 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Operator supporting the verification of RSASSA-PKCS1-v1_5 signatures using the BC light-weight API.
+ /// </summary>
+ public class BcTlsRsaVerifier
+ : BcTlsVerifier
+ {
+ public BcTlsRsaVerifier(BcTlsCrypto crypto, RsaKeyParameters publicKey)
+ : base(crypto, publicKey)
+ {
+ }
+
+ public override bool VerifyRawSignature(DigitallySigned signedParams, byte[] hash)
+ {
+ IDigest nullDigest = new NullDigest();
+
+ SignatureAndHashAlgorithm algorithm = signedParams.Algorithm;
+ ISigner signer;
+ if (algorithm != null)
+ {
+ if (algorithm.Signature != SignatureAlgorithm.rsa)
+ throw new InvalidOperationException("Invalid algorithm: " + algorithm);
+
+ /*
+ * 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].
+ */
+ signer = new RsaDigestSigner(nullDigest, 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.
+ */
+ signer = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), nullDigest);
+ }
+ signer.Init(false, m_publicKey);
+ signer.BlockUpdate(hash, 0, hash.Length);
+ return signer.VerifySignature(signedParams.Signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs b/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs
new file mode 100644
index 000000000..b76c1612a
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs
@@ -0,0 +1,242 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>BC light-weight support class for handling TLS secrets and deriving key material and other secrets
+ /// from them.</summary>
+ public class BcTlsSecret
+ : AbstractTlsSecret
+ {
+ // SSL3 magic mix constants ("A", "BB", "CCC", ...)
+ private static readonly byte[] Ssl3Const = GenerateSsl3Constants();
+
+ private static byte[] GenerateSsl3Constants()
+ {
+ int n = 15;
+ byte[] result = new byte[n * (n + 1) / 2];
+ int pos = 0;
+ for (int i = 0; i < n; ++i)
+ {
+ byte b = (byte)('A' + i);
+ for (int j = 0; j <= i; ++j)
+ {
+ result[pos++] = b;
+ }
+ }
+ return result;
+ }
+
+ protected readonly BcTlsCrypto m_crypto;
+
+ public BcTlsSecret(BcTlsCrypto crypto, byte[] data)
+ : base(data)
+ {
+ this.m_crypto = crypto;
+ }
+
+ public override TlsSecret DeriveUsingPrf(int prfAlgorithm, string label, byte[] seed, int length)
+ {
+ lock (this)
+ {
+ CheckAlive();
+
+ switch (prfAlgorithm)
+ {
+ case PrfAlgorithm.tls13_hkdf_sha256:
+ return TlsCryptoUtilities.HkdfExpandLabel(this, CryptoHashAlgorithm.sha256, label, seed, length);
+ case PrfAlgorithm.tls13_hkdf_sha384:
+ return TlsCryptoUtilities.HkdfExpandLabel(this, CryptoHashAlgorithm.sha384, label, seed, length);
+ case PrfAlgorithm.tls13_hkdf_sm3:
+ return TlsCryptoUtilities.HkdfExpandLabel(this, CryptoHashAlgorithm.sm3, label, seed, length);
+ default:
+ return m_crypto.AdoptLocalSecret(Prf(prfAlgorithm, label, seed, length));
+ }
+ }
+ }
+
+ public override TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length)
+ {
+ lock (this)
+ {
+ if (length < 1)
+ return m_crypto.AdoptLocalSecret(TlsUtilities.EmptyBytes);
+
+ int hashLen = TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm);
+ if (length > (255 * hashLen))
+ throw new ArgumentException("must be <= 255 * (output size of 'hashAlgorithm')", "length");
+
+ CheckAlive();
+
+ byte[] prk = m_data;
+
+ HMac hmac = new HMac(m_crypto.CreateDigest(cryptoHashAlgorithm));
+ hmac.Init(new KeyParameter(prk));
+
+ byte[] okm = new byte[length];
+
+ byte[] t = new byte[hashLen];
+ byte counter = 0x00;
+
+ int pos = 0;
+ for (; ; )
+ {
+ hmac.BlockUpdate(info, 0, info.Length);
+ hmac.Update((byte)++counter);
+ hmac.DoFinal(t, 0);
+
+ int remaining = length - pos;
+ if (remaining <= hashLen)
+ {
+ Array.Copy(t, 0, okm, pos, remaining);
+ break;
+ }
+
+ Array.Copy(t, 0, okm, pos, hashLen);
+ pos += hashLen;
+ hmac.BlockUpdate(t, 0, t.Length);
+ }
+
+ return m_crypto.AdoptLocalSecret(okm);
+ }
+ }
+
+ public override TlsSecret HkdfExtract(int cryptoHashAlgorithm, byte[] ikm)
+ {
+ lock (this)
+ {
+ CheckAlive();
+
+ byte[] salt = m_data;
+ this.m_data = null;
+
+ HMac hmac = new HMac(m_crypto.CreateDigest(cryptoHashAlgorithm));
+ hmac.Init(new KeyParameter(salt));
+
+ hmac.BlockUpdate(ikm, 0, ikm.Length);
+
+ byte[] prk = new byte[hmac.GetMacSize()];
+ hmac.DoFinal(prk, 0);
+
+ return m_crypto.AdoptLocalSecret(prk);
+ }
+ }
+
+ protected override AbstractTlsCrypto Crypto
+ {
+ get { return m_crypto; }
+ }
+
+ protected virtual void HmacHash(IDigest digest, byte[] secret, int secretOff, int secretLen, byte[] seed,
+ byte[] output)
+ {
+ HMac mac = new HMac(digest);
+ mac.Init(new KeyParameter(secret, secretOff, secretLen));
+
+ byte[] a = seed;
+
+ int macSize = mac.GetMacSize();
+
+ byte[] b1 = new byte[macSize];
+ byte[] b2 = new byte[macSize];
+
+ int pos = 0;
+ while (pos < output.Length)
+ {
+ mac.BlockUpdate(a, 0, a.Length);
+ mac.DoFinal(b1, 0);
+ a = b1;
+ mac.BlockUpdate(a, 0, a.Length);
+ mac.BlockUpdate(seed, 0, seed.Length);
+ mac.DoFinal(b2, 0);
+ Array.Copy(b2, 0, output, pos, System.Math.Min(macSize, output.Length - pos));
+ pos += macSize;
+ }
+ }
+
+ protected virtual byte[] Prf(int prfAlgorithm, string label, byte[] seed, int length)
+ {
+ if (PrfAlgorithm.ssl_prf_legacy == prfAlgorithm)
+ return Prf_Ssl(seed, length);
+
+ byte[] labelSeed = Arrays.Concatenate(Strings.ToByteArray(label), seed);
+
+ if (PrfAlgorithm.tls_prf_legacy == prfAlgorithm)
+ return Prf_1_0(labelSeed, length);
+
+ return Prf_1_2(prfAlgorithm, labelSeed, length);
+ }
+
+ protected virtual byte[] Prf_Ssl(byte[] seed, int length)
+ {
+ IDigest md5 = m_crypto.CreateDigest(CryptoHashAlgorithm.md5);
+ IDigest sha1 = m_crypto.CreateDigest(CryptoHashAlgorithm.sha1);
+
+ int md5Size = md5.GetDigestSize();
+ int sha1Size = sha1.GetDigestSize();
+
+ byte[] tmp = new byte[System.Math.Max(md5Size, sha1Size)];
+ byte[] result = new byte[length];
+
+ int constLen = 1, constPos = 0, resultPos = 0;
+ while (resultPos < length)
+ {
+ sha1.BlockUpdate(Ssl3Const, constPos, constLen);
+ constPos += constLen++;
+
+ sha1.BlockUpdate(m_data, 0, m_data.Length);
+ sha1.BlockUpdate(seed, 0, seed.Length);
+ sha1.DoFinal(tmp, 0);
+
+ md5.BlockUpdate(m_data, 0, m_data.Length);
+ md5.BlockUpdate(tmp, 0, sha1Size);
+
+ int remaining = length - resultPos;
+ if (remaining < md5Size)
+ {
+ md5.DoFinal(tmp, 0);
+ Array.Copy(tmp, 0, result, resultPos, remaining);
+ resultPos += remaining;
+ }
+ else
+ {
+ md5.DoFinal(result, resultPos);
+ resultPos += md5Size;
+ }
+ }
+
+ return result;
+ }
+
+ protected virtual byte[] Prf_1_0(byte[] labelSeed, int length)
+ {
+ int s_half = (m_data.Length + 1) / 2;
+
+ IDigest md5 = m_crypto.CreateDigest(CryptoHashAlgorithm.md5);
+ byte[] b1 = new byte[length];
+ HmacHash(md5, m_data, 0, s_half, labelSeed, b1);
+
+ IDigest sha1 = m_crypto.CreateDigest(CryptoHashAlgorithm.sha1);
+ byte[] b2 = new byte[length];
+ HmacHash(sha1, m_data, m_data.Length - s_half, s_half, labelSeed, b2);
+
+ for (int i = 0; i < length; i++)
+ {
+ b1[i] ^= b2[i];
+ }
+ return b1;
+ }
+
+ protected virtual byte[] Prf_1_2(int prfAlgorithm, byte[] labelSeed, int length)
+ {
+ IDigest digest = m_crypto.CreateDigest(TlsCryptoUtilities.GetHashForPrf(prfAlgorithm));
+ byte[] result = new byte[length];
+ HmacHash(digest, m_data, 0, m_data.Length, labelSeed, result);
+ return result;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsSigner.cs
new file mode 100644
index 000000000..841803043
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsSigner.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public abstract class BcTlsSigner
+ : TlsSigner
+ {
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly AsymmetricKeyParameter m_privateKey;
+
+ protected BcTlsSigner(BcTlsCrypto crypto, AsymmetricKeyParameter privateKey)
+ {
+ if (crypto == null)
+ throw new ArgumentNullException("crypto");
+ if (privateKey == null)
+ throw new ArgumentNullException("privateKey");
+ if (!privateKey.IsPrivate)
+ throw new ArgumentException("must be private", "privateKey");
+
+ this.m_crypto = crypto;
+ this.m_privateKey = privateKey;
+ }
+
+ public abstract byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash);
+
+ public virtual TlsStreamSigner GetStreamSigner(SignatureAndHashAlgorithm algorithm)
+ {
+ return null;
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsSrp6Client.cs b/crypto/src/tls/crypto/impl/bc/BcTlsSrp6Client.cs
new file mode 100644
index 000000000..de4b9bd50
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsSrp6Client.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsSrp6Client
+ : TlsSrp6Client
+ {
+ private readonly Srp6Client m_srp6Client;
+
+ internal BcTlsSrp6Client(Srp6Client srpClient)
+ {
+ this.m_srp6Client = srpClient;
+ }
+
+ public BigInteger CalculateSecret(BigInteger serverB)
+ {
+ try
+ {
+ return m_srp6Client.CalculateSecret(serverB);
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
+ }
+ }
+
+ public BigInteger GenerateClientCredentials(byte[] srpSalt, byte[] identity, byte[] password)
+ {
+ return m_srp6Client.GenerateClientCredentials(srpSalt, identity, password);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsSrp6Server.cs b/crypto/src/tls/crypto/impl/bc/BcTlsSrp6Server.cs
new file mode 100644
index 000000000..8ee79f627
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsSrp6Server.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsSrp6Server
+ : TlsSrp6Server
+ {
+ private readonly Srp6Server m_srp6Server;
+
+ internal BcTlsSrp6Server(Srp6Server srp6Server)
+ {
+ this.m_srp6Server = srp6Server;
+ }
+
+ public BigInteger GenerateServerCredentials()
+ {
+ return m_srp6Server.GenerateServerCredentials();
+ }
+
+ public BigInteger CalculateSecret(BigInteger clientA)
+ {
+ try
+ {
+ return m_srp6Server.CalculateSecret(clientA);
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsSrp6VerifierGenerator.cs b/crypto/src/tls/crypto/impl/bc/BcTlsSrp6VerifierGenerator.cs
new file mode 100644
index 000000000..3d9e23a29
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsSrp6VerifierGenerator.cs
@@ -0,0 +1,23 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsSrp6VerifierGenerator
+ : TlsSrp6VerifierGenerator
+ {
+ private readonly Srp6VerifierGenerator m_srp6VerifierGenerator;
+
+ internal BcTlsSrp6VerifierGenerator(Srp6VerifierGenerator srp6VerifierGenerator)
+ {
+ this.m_srp6VerifierGenerator = srp6VerifierGenerator;
+ }
+
+ public BigInteger GenerateVerifier(byte[] salt, byte[] identity, byte[] password)
+ {
+ return m_srp6VerifierGenerator.GenerateVerifier(salt, identity, password);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsStreamSigner.cs b/crypto/src/tls/crypto/impl/bc/BcTlsStreamSigner.cs
new file mode 100644
index 000000000..158fb053e
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsStreamSigner.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsStreamSigner
+ : TlsStreamSigner
+ {
+ private readonly SignerSink m_output;
+
+ internal BcTlsStreamSigner(ISigner signer)
+ {
+ this.m_output = new SignerSink(signer);
+ }
+
+ public Stream GetOutputStream()
+ {
+ return m_output;
+ }
+
+ public byte[] GetSignature()
+ {
+ try
+ {
+ return m_output.Signer.GenerateSignature();
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsStreamVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsStreamVerifier.cs
new file mode 100644
index 000000000..0848a30dd
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsStreamVerifier.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcTlsStreamVerifier
+ : TlsStreamVerifier
+ {
+ private readonly SignerSink m_output;
+ private readonly byte[] m_signature;
+
+ internal BcTlsStreamVerifier(ISigner verifier, byte[] signature)
+ {
+ this.m_output = new SignerSink(verifier);
+ this.m_signature = signature;
+ }
+
+ public Stream GetOutputStream()
+ {
+ return m_output;
+ }
+
+ public bool IsVerified()
+ {
+ return m_output.Signer.VerifySignature(m_signature);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsVerifier.cs b/crypto/src/tls/crypto/impl/bc/BcTlsVerifier.cs
new file mode 100644
index 000000000..dc8d21d74
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsVerifier.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public abstract class BcTlsVerifier
+ : TlsVerifier
+ {
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly AsymmetricKeyParameter m_publicKey;
+
+ protected BcTlsVerifier(BcTlsCrypto crypto, AsymmetricKeyParameter publicKey)
+ {
+ if (crypto == null)
+ throw new ArgumentNullException("crypto");
+ if (publicKey == null)
+ throw new ArgumentNullException("publicKey");
+ if (publicKey.IsPrivate)
+ throw new ArgumentException("must be public", "publicKey");
+
+ this.m_crypto = crypto;
+ this.m_publicKey = publicKey;
+ }
+
+ public virtual TlsStreamVerifier GetStreamVerifier(DigitallySigned signature)
+ {
+ return null;
+ }
+
+ public abstract bool VerifyRawSignature(DigitallySigned signature, byte[] hash);
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcVerifyingStreamSigner.cs b/crypto/src/tls/crypto/impl/bc/BcVerifyingStreamSigner.cs
new file mode 100644
index 000000000..4d9506857
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcVerifyingStreamSigner.cs
@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ internal sealed class BcVerifyingStreamSigner
+ : TlsStreamSigner
+ {
+ private readonly ISigner m_signer;
+ private readonly ISigner m_verifier;
+ private readonly TeeOutputStream m_output;
+
+ internal BcVerifyingStreamSigner(ISigner signer, ISigner verifier)
+ {
+ Stream outputSigner = new SignerSink(signer);
+ Stream outputVerifier = new SignerSink(verifier);
+
+ this.m_signer = signer;
+ this.m_verifier = verifier;
+ this.m_output = new TeeOutputStream(outputSigner, outputVerifier);
+ }
+
+ public Stream GetOutputStream()
+ {
+ return m_output;
+ }
+
+ public byte[] GetSignature()
+ {
+ try
+ {
+ byte[] signature = m_signer.GenerateSignature();
+ if (m_verifier.VerifySignature(signature))
+ return signature;
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcX25519.cs b/crypto/src/tls/crypto/impl/bc/BcX25519.cs
new file mode 100644
index 000000000..c26bc5155
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcX25519.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Rfc7748;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Support class for X25519 using the BC light-weight library.</summary>
+ public class BcX25519
+ : TlsAgreement
+ {
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly byte[] m_privateKey = new byte[X25519.ScalarSize];
+ protected readonly byte[] m_peerPublicKey = new byte[X25519.PointSize];
+
+ public BcX25519(BcTlsCrypto crypto)
+ {
+ this.m_crypto = crypto;
+ }
+
+ public virtual byte[] GenerateEphemeral()
+ {
+ m_crypto.SecureRandom.NextBytes(m_privateKey);
+
+ byte[] publicKey = new byte[X25519.PointSize];
+ X25519.ScalarMultBase(m_privateKey, 0, publicKey, 0);
+ return publicKey;
+ }
+
+ public virtual void ReceivePeerValue(byte[] peerValue)
+ {
+ if (peerValue == null || peerValue.Length != X25519.PointSize)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ Array.Copy(peerValue, 0, m_peerPublicKey, 0, X25519.PointSize);
+ }
+
+ public virtual TlsSecret CalculateSecret()
+ {
+ try
+ {
+ byte[] secret = new byte[X25519.PointSize];
+ if (!X25519.CalculateAgreement(m_privateKey, 0, m_peerPublicKey, 0, secret, 0))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ return m_crypto.AdoptLocalSecret(secret);
+ }
+ finally
+ {
+ Array.Clear(m_privateKey, 0, m_privateKey.Length);
+ Array.Clear(m_peerPublicKey, 0, m_peerPublicKey.Length);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcX25519Domain.cs b/crypto/src/tls/crypto/impl/bc/BcX25519Domain.cs
new file mode 100644
index 000000000..58767cfce
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcX25519Domain.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public class BcX25519Domain
+ : TlsECDomain
+ {
+ protected readonly BcTlsCrypto m_crypto;
+
+ public BcX25519Domain(BcTlsCrypto crypto)
+ {
+ this.m_crypto = crypto;
+ }
+
+ public virtual TlsAgreement CreateECDH()
+ {
+ return new BcX25519(m_crypto);
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcX448.cs b/crypto/src/tls/crypto/impl/bc/BcX448.cs
new file mode 100644
index 000000000..ebeba4e8f
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcX448.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Rfc7748;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ /// <summary>Support class for X448 using the BC light-weight library.</summary>
+ public class BcX448
+ : TlsAgreement
+ {
+ protected readonly BcTlsCrypto m_crypto;
+ protected readonly byte[] m_privateKey = new byte[X448.ScalarSize];
+ protected readonly byte[] m_peerPublicKey = new byte[X448.PointSize];
+
+ public BcX448(BcTlsCrypto crypto)
+ {
+ this.m_crypto = crypto;
+ }
+
+ public virtual byte[] GenerateEphemeral()
+ {
+ m_crypto.SecureRandom.NextBytes(m_privateKey);
+
+ byte[] publicKey = new byte[X448.PointSize];
+ X448.ScalarMultBase(m_privateKey, 0, publicKey, 0);
+ return publicKey;
+ }
+
+ public virtual void ReceivePeerValue(byte[] peerValue)
+ {
+ if (peerValue == null || peerValue.Length != X448.PointSize)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ Array.Copy(peerValue, 0, m_peerPublicKey, 0, X448.PointSize);
+ }
+
+ public virtual TlsSecret CalculateSecret()
+ {
+ try
+ {
+ byte[] secret = new byte[X448.PointSize];
+ if (!X448.CalculateAgreement(m_privateKey, 0, m_peerPublicKey, 0, secret, 0))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ return m_crypto.AdoptLocalSecret(secret);
+ }
+ finally
+ {
+ Array.Clear(m_privateKey, 0, m_privateKey.Length);
+ Array.Clear(m_peerPublicKey, 0, m_peerPublicKey.Length);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/crypto/impl/bc/BcX448Domain.cs b/crypto/src/tls/crypto/impl/bc/BcX448Domain.cs
new file mode 100644
index 000000000..757fa8fcc
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcX448Domain.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+ public class BcX448Domain
+ : TlsECDomain
+ {
+ protected readonly BcTlsCrypto m_crypto;
+
+ public BcX448Domain(BcTlsCrypto crypto)
+ {
+ this.m_crypto = crypto;
+ }
+
+ public virtual TlsAgreement CreateECDH()
+ {
+ return new BcX448(m_crypto);
+ }
+ }
+}
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index 0c0184d42..c5fdecd54 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -467,6 +467,48 @@
<Compile Include="src\test\nist\NistCertPathTest.cs" />
<Compile Include="src\test\nist\NistCertPathTest2.cs" />
<Compile Include="src\test\rsa3\RSA3CertTest.cs" />
+ <Compile Include="src\tls\crypto\test\BcTlsCryptoTest.cs" />
+ <Compile Include="src\tls\test\ByteQueueInputStreamTest.cs" />
+ <Compile Include="src\tls\test\CertChainUtilities.cs" />
+ <Compile Include="src\tls\test\DtlsProtocolTest.cs" />
+ <Compile Include="src\tls\test\DtlsPskProtocolTest.cs" />
+ <Compile Include="src\tls\test\DtlsTestCase.cs" />
+ <Compile Include="src\tls\test\DtlsTestClientProtocol.cs" />
+ <Compile Include="src\tls\test\DtlsTestServerProtocol.cs" />
+ <Compile Include="src\tls\test\DtlsTestSuite.cs" />
+ <Compile Include="src\tls\test\LoggingDatagramTransport.cs" />
+ <Compile Include="src\tls\test\MockDatagramAssociation.cs" />
+ <Compile Include="src\tls\test\MockDtlsClient.cs" />
+ <Compile Include="src\tls\test\MockDtlsServer.cs" />
+ <Compile Include="src\tls\test\MockPskDtlsClient.cs" />
+ <Compile Include="src\tls\test\MockPskDtlsServer.cs" />
+ <Compile Include="src\tls\test\MockPskTlsClient.cs" />
+ <Compile Include="src\tls\test\MockPskTlsServer.cs" />
+ <Compile Include="src\tls\test\MockSrpTlsClient.cs" />
+ <Compile Include="src\tls\test\MockSrpTlsServer.cs" />
+ <Compile Include="src\tls\test\MockTlsClient.cs" />
+ <Compile Include="src\tls\test\MockTlsServer.cs" />
+ <Compile Include="src\tls\test\NetworkStream.cs" />
+ <Compile Include="src\tls\test\PipedStream.cs" />
+ <Compile Include="src\tls\test\PrfTest.cs" />
+ <Compile Include="src\tls\test\PskTlsClientTest.cs" />
+ <Compile Include="src\tls\test\PskTlsServerTest.cs" />
+ <Compile Include="src\tls\test\TlsClientTest.cs" />
+ <Compile Include="src\tls\test\TlsProtocolNonBlockingTest.cs" />
+ <Compile Include="src\tls\test\TlsProtocolTest.cs" />
+ <Compile Include="src\tls\test\TlsPskProtocolTest.cs" />
+ <Compile Include="src\tls\test\TlsServerTest.cs" />
+ <Compile Include="src\tls\test\TlsSrpProtocolTest.cs" />
+ <Compile Include="src\tls\test\TlsTestCase.cs" />
+ <Compile Include="src\tls\test\TlsTestClientImpl.cs" />
+ <Compile Include="src\tls\test\TlsTestClientProtocol.cs" />
+ <Compile Include="src\tls\test\TlsTestConfig.cs" />
+ <Compile Include="src\tls\test\TlsTestServerImpl.cs" />
+ <Compile Include="src\tls\test\TlsTestServerProtocol.cs" />
+ <Compile Include="src\tls\test\TlsTestSuite.cs" />
+ <Compile Include="src\tls\test\TlsTestUtilities.cs" />
+ <Compile Include="src\tls\test\TlsUtilitiesTest.cs" />
+ <Compile Include="src\tls\test\UnreliableDatagramTransport.cs" />
<Compile Include="src\tsp\test\AllTests.cs" />
<Compile Include="src\tsp\test\GenTimeAccuracyTest.cs" />
<Compile Include="src\tsp\test\ParseTest.cs" />
diff --git a/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs b/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs
new file mode 100644
index 000000000..a57212c73
--- /dev/null
+++ b/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs
@@ -0,0 +1,820 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Tls.Tests;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Tests
+{
+ [TestFixture]
+ public class BcTlsCryptoTest
+ {
+ private static readonly byte[] ClientHello = Hex("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63"
+ + "ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83"
+ + "02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b"
+ + "00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00"
+ + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23"
+ + "00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2"
+ + "3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a"
+ + "af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03"
+ + "02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06"
+ + "02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01");
+ private static readonly byte[] ServerHello = Hex("02 00 00 56 03 03 a6 af 06 a4 12 18 60"
+ + "dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e"
+ + "d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88"
+ + "76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1"
+ + "dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04");
+ private static readonly byte[] EncryptedExtensions = Hex("08 00 00 24 00 22 00 0a 00 14 00"
+ + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c"
+ + "00 02 40 01 00 00 00 00");
+ private static readonly byte[] Certificate = Hex("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82"
+ + "01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48"
+ + "86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03"
+ + "72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17"
+ + "0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06"
+ + "03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7"
+ + "0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f"
+ + "82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26"
+ + "d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c"
+ + "1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52"
+ + "4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74"
+ + "80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93"
+ + "ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03"
+ + "01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06"
+ + "03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01"
+ + "01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a"
+ + "72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea"
+ + "e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01"
+ + "51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be"
+ + "c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b"
+ + "1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8"
+ + "96 12 29 ac 91 87 b4 2b 4d e1 00 00");
+ private static readonly byte[] CertificateVerify = Hex("0f 00 00 84 08 04 00 80 5a 74 7c"
+ + "5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a"
+ + "b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07"
+ + "86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b"
+ + "be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44"
+ + "5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a"
+ + "3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3");
+ private static readonly byte[] ServerFinished = Hex("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb"
+ + "dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07"
+ + "18");
+ private static readonly byte[] ClientFinished = Hex("14 00 00 20 a8 ec 43 6d 67 76 34 ae 52 5a"
+ + "c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce"
+ + "61");
+
+ private readonly TlsCrypto m_crypto = new BcTlsCrypto(new SecureRandom());
+
+ protected TlsCredentialedSigner LoadCredentialedSigner(TlsCryptoParameters cryptoParams, string resource,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ return TlsTestUtilities.LoadSignerCredentials(cryptoParams, m_crypto,
+ new string[]{ "x509-server-" + resource + ".pem" }, "x509-server-key-" + resource + ".pem",
+ signatureAndHashAlgorithm);
+ }
+
+ protected TlsCredentialedSigner LoadCredentialedSignerLegacy(TlsCryptoParameters cryptoParams,
+ short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.dsa:
+ return LoadCredentialedSigner(cryptoParams, "dsa", null);
+ case SignatureAlgorithm.ecdsa:
+ return LoadCredentialedSigner(cryptoParams, "ecdsa", null);
+ case SignatureAlgorithm.rsa:
+ return LoadCredentialedSigner(cryptoParams, "rsa-sign", null);
+ default:
+ return null;
+ }
+ }
+
+ protected TlsCredentialedSigner LoadCredentialedSigner12(TlsCryptoParameters cryptoParams,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ switch (signatureAndHashAlgorithm.Signature)
+ {
+ case SignatureAlgorithm.dsa:
+ return LoadCredentialedSigner(cryptoParams, "dsa", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.ecdsa:
+ return LoadCredentialedSigner(cryptoParams, "ecdsa", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.ed25519:
+ return LoadCredentialedSigner(cryptoParams, "ed25519", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.ed448:
+ return LoadCredentialedSigner(cryptoParams, "ed448", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_256", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_384", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_512", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm);
+
+ // TODO[draft-smyshlyaev-tls12-gost-suites-10] Add test resources for these
+ case SignatureAlgorithm.gostr34102012_256:
+ case SignatureAlgorithm.gostr34102012_512:
+
+ default:
+ return null;
+ }
+ }
+
+ protected TlsCredentialedSigner LoadCredentialedSigner13(TlsCryptoParameters cryptoParams, int signatureScheme)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureScheme.GetSignatureAndHashAlgorithm(
+ signatureScheme);
+
+ switch (signatureScheme)
+ {
+ case SignatureScheme.ecdsa_secp256r1_sha256:
+ return LoadCredentialedSigner(cryptoParams, "ecdsa", signatureAndHashAlgorithm);
+ case SignatureScheme.ed25519:
+ return LoadCredentialedSigner(cryptoParams, "ed25519", signatureAndHashAlgorithm);
+ case SignatureScheme.ed448:
+ return LoadCredentialedSigner(cryptoParams, "ed448", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_pss_sha256:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_256", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_pss_sha384:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_384", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_pss_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_512", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_rsae_sha256:
+ case SignatureScheme.rsa_pss_rsae_sha384:
+ case SignatureScheme.rsa_pss_rsae_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm);
+
+ // TODO[tls] Add test resources for these
+ case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256:
+ case SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384:
+ case SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512:
+ case SignatureScheme.ecdsa_secp384r1_sha384:
+ case SignatureScheme.ecdsa_secp521r1_sha512:
+ case SignatureScheme.sm2sig_sm3:
+
+ default:
+ return null;
+ }
+ }
+
+ [Test]
+ public void TestDHDomain()
+ {
+ if (!m_crypto.HasDHAgreement())
+ {
+ return;
+ }
+
+ for (int namedGroup = 256; namedGroup < 512; ++namedGroup)
+ {
+ if (!NamedGroup.RefersToASpecificFiniteField(namedGroup) || !m_crypto.HasNamedGroup(namedGroup))
+ {
+ continue;
+ }
+
+ ImplTestDHDomain(new TlsDHConfig(namedGroup, false));
+ ImplTestDHDomain(new TlsDHConfig(namedGroup, true));
+ }
+
+ IList groups = new TestTlsDHGroupVerifier().Groups;
+ foreach (DHGroup dhGroup in groups)
+ {
+ int namedGroup = TlsDHUtilities.GetNamedGroupForDHParameters(dhGroup.P, dhGroup.G);
+ if (NamedGroup.RefersToASpecificFiniteField(namedGroup))
+ {
+ // Already tested the named groups
+ continue;
+ }
+
+ ImplTestDHDomain(new TlsDHConfig(dhGroup));
+ }
+ }
+
+ [Test]
+ public void TestECDomain()
+ {
+ if (!m_crypto.HasECDHAgreement())
+ {
+ return;
+ }
+
+ for (int namedGroup = 0; namedGroup < 256; ++namedGroup)
+ {
+ if (!NamedGroup.RefersToAnECDHCurve(namedGroup) || !m_crypto.HasNamedGroup(namedGroup))
+ {
+ continue;
+ }
+
+ ImplTestECDomain(new TlsECConfig(namedGroup));
+ }
+ }
+
+ [Test]
+ public void TestHkdf()
+ {
+ /*
+ * Test vectors drawn from the server-side calculations of example handshake trace in RFC 8448, section 3.
+ */
+
+ int hash = CryptoHashAlgorithm.sha256;
+ int hashLen = TlsCryptoUtilities.GetHashOutputSize(hash);
+
+ TlsSecret init = m_crypto.HkdfInit(hash), early, handshake, master, c_hs_t, s_hs_t, c_ap_t, s_ap_t, exp_master, res_master;
+
+ TlsHash prfHash = m_crypto.CreateHash(hash);
+
+ byte[] emptyTranscriptHash = GetCurrentHash(prfHash);
+ Expect(emptyTranscriptHash, "e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55");
+
+ // {server} extract secret "early":
+ {
+ byte[] ikm = new byte[32];
+ early = init.HkdfExtract(hash, ikm);
+ Expect(early, "33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a");
+ }
+
+ // {server} derive secret for handshake "tls13 derived":
+ {
+ string label = "derived";
+ handshake = TlsCryptoUtilities.HkdfExpandLabel(early, hash, label, emptyTranscriptHash, hashLen);
+ Expect(handshake, "6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba");
+ }
+
+ // {server} extract secret "handshake":
+ {
+ byte[] ikm = Hex("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d");
+ handshake = handshake.HkdfExtract(hash, ikm);
+ Expect(handshake, "1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac");
+ }
+
+ prfHash.Update(ClientHello, 0, ClientHello.Length);
+ prfHash.Update(ServerHello, 0, ServerHello.Length);
+
+ byte[] serverHelloTranscriptHash = GetCurrentHash(prfHash);
+ Expect(serverHelloTranscriptHash, "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8");
+
+ // {server} derive secret "tls13 c hs traffic":
+ {
+ string label = "c hs traffic";
+ c_hs_t = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, serverHelloTranscriptHash, hashLen);
+ Expect(c_hs_t, "b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21");
+ }
+
+ // {server} derive secret "tls13 s hs traffic":
+ {
+ string label = "s hs traffic";
+ s_hs_t = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, serverHelloTranscriptHash, hashLen);
+ Expect(s_hs_t, "b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38");
+ }
+
+ // {server} derive secret for master "tls13 derived":
+ {
+ string label = "derived";
+ master = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, emptyTranscriptHash, hashLen);
+ Expect(master, "43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4");
+ }
+
+ // {server} extract secret "master":
+ {
+ byte[] ikm = new byte[32];
+ master = master.HkdfExtract(hash, ikm);
+ Expect(master, "18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19");
+ }
+
+ // {server} derive write traffic keys for handshake data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "5d 31 3e b2 67 12 76 ee 13 00 0b 30");
+ }
+
+ prfHash.Update(EncryptedExtensions, 0, EncryptedExtensions.Length);
+ prfHash.Update(Certificate, 0, Certificate.Length);
+ prfHash.Update(CertificateVerify, 0, CertificateVerify.Length);
+
+ // {server} calculate (server) finished "tls13 finished":
+ {
+ TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "finished", TlsUtilities.EmptyBytes, hashLen);
+ Expect(expanded, "00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8");
+
+ // TODO Mention this transcript hash in RFC 8448 data?
+ byte[] transcriptHash = GetCurrentHash(prfHash);
+ Expect(transcriptHash, "ed b7 72 5f a7 a3 47 3b 03 1e c8 ef 65 a2 48 54 93 90 01 38 a2 b9 12 91 40 7d 79 51 a0 61 10 ed");
+
+ byte[] finished = CalculateHmac(hash, expanded, transcriptHash);
+ Expect(finished, Hex("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"));
+ }
+
+ prfHash.Update(ServerFinished, 0, ServerFinished.Length);
+
+ byte[] serverFinishedTranscriptHash = GetCurrentHash(prfHash);
+ Expect(serverFinishedTranscriptHash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13");
+
+ // {server} derive secret "tls13 c ap traffic":
+ {
+ string label = "c ap traffic";
+ c_ap_t = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen);
+ Expect(c_ap_t, "9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5");
+ }
+
+ // {server} derive secret "tls13 s ap traffic":
+ {
+ string label = "s ap traffic";
+ s_ap_t = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen);
+ Expect(s_ap_t, "a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43");
+ }
+
+ // {server} derive secret "tls13 exp master":
+ {
+ string label = "exp master";
+ exp_master = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen);
+ Expect(exp_master, "fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50");
+ }
+
+ // {server} derive write traffic keys for application data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(s_ap_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(s_ap_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "cf 78 2b 88 dd 83 54 9a ad f1 e9 84");
+ }
+
+ // {server} derive read traffic keys for handshake data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f");
+ }
+
+ // {server} calculate (client) finished "tls13 finished":
+ {
+ TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "finished", TlsUtilities.EmptyBytes, hashLen);
+ Expect(expanded, "b8 0a d0 10 15 fb 2f 0b d6 5f f7 d4 da 5d 6b f8 3f 84 82 1d 1f 87 fd c7 d3 c7 5b 5a 7b 42 d9 c4");
+
+ // TODO Mention this transcript hash in RFC 8448 data?
+ byte[] finished = CalculateHmac(hash, expanded, serverFinishedTranscriptHash);
+ Expect(finished, Hex("a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"));
+ }
+
+ prfHash.Update(ClientFinished, 0, ClientFinished.Length);
+
+ byte[] clientFinishedTranscriptHash = GetCurrentHash(prfHash);
+ Expect(clientFinishedTranscriptHash, "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d");
+
+ // {server} derive read traffic keys for application data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(c_ap_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 3f 50 51");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(c_ap_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "5b 78 92 3d ee 08 57 90 33 e5 23 d9");
+ }
+
+ // {server} derive secret "tls13 res master":
+ {
+ res_master = TlsCryptoUtilities.HkdfExpandLabel(master, hash, "res master", clientFinishedTranscriptHash, hashLen);
+ Expect(res_master, "7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c");
+ }
+
+ // {server} generate resumption secret "tls13 resumption":
+ {
+ byte[] context = Hex("00 00");
+ TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(res_master, hash, "resumption", context, hashLen);
+ Expect(expanded, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3");
+ }
+ }
+
+ [Test]
+ public void TestHkdfExpandLimit()
+ {
+ int[] hashes = new int[] { CryptoHashAlgorithm.md5, CryptoHashAlgorithm.sha1, CryptoHashAlgorithm.sha224,
+ CryptoHashAlgorithm.sha256, CryptoHashAlgorithm.sha384, CryptoHashAlgorithm.sha512,
+ CryptoHashAlgorithm.sm3 };
+
+ for (int i = 0; i < hashes.Length; ++i)
+ {
+ int hash = hashes[i];
+ int hashLen = TlsCryptoUtilities.GetHashOutputSize(hash);
+ byte[] zeroes = new byte[hashLen];
+
+ int limit = 255 * hashLen;
+
+ TlsSecret secret = m_crypto.HkdfInit(hash).HkdfExtract(hash, zeroes);
+
+ try
+ {
+ secret.HkdfExpand(hash, TlsUtilities.EmptyBytes, limit);
+ }
+ catch (Exception e)
+ {
+ Assert.Fail("Unexpected exception: " + e.Message);
+ }
+
+ try
+ {
+ secret.HkdfExpand(hash, TlsUtilities.EmptyBytes, limit + 1);
+ Assert.Fail("Expected an exception!");
+ }
+ catch (ArgumentException)
+ {
+ // Expected
+ }
+ catch (Exception e)
+ {
+ Assert.Fail("Unexpected exception: " + e.Message);
+ }
+ }
+ }
+
+ [Test]
+ public void TestSignaturesLegacy()
+ {
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.dsa, SignatureAlgorithm.ecdsa,
+ SignatureAlgorithm.rsa };
+
+ TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv11);
+
+ for (int i = 0; i < signatureAlgorithms.Length; ++i)
+ {
+ short signatureAlgorithm = signatureAlgorithms[i];
+ if (!m_crypto.HasSignatureAlgorithm(signatureAlgorithm))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSignerLegacy(cryptoParams,
+ signatureAlgorithm);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignatureLegacy(credentialedSigner);
+ }
+ }
+ }
+
+ [Test]
+ public void TestSignatures12()
+ {
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.md5, HashAlgorithm.sha1, HashAlgorithm.sha224,
+ HashAlgorithm.sha256, HashAlgorithm.sha384, HashAlgorithm.sha512 };
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.dsa, SignatureAlgorithm.ecdsa,
+ SignatureAlgorithm.rsa };
+
+ TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv12);
+
+ for (int i = 0; i < signatureAlgorithms.Length; ++i)
+ {
+ short signatureAlgorithm = signatureAlgorithms[i];
+
+ for (int j = 0; j < hashAlgorithms.Length; ++j)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureAndHashAlgorithm.GetInstance(
+ hashAlgorithms[j], signatureAlgorithm);
+ if (!m_crypto.HasSignatureAndHashAlgorithm(signatureAndHashAlgorithm))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner12(cryptoParams,
+ signatureAndHashAlgorithm);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignature12(credentialedSigner, signatureAndHashAlgorithm);
+ }
+ }
+ }
+
+ // Signature algorithms usable with HashAlgorithm.Intrinsic in TLS 1.2
+ short[] intrinsicSignatureAlgorithms = new short[] { SignatureAlgorithm.ed25519, SignatureAlgorithm.ed448,
+ SignatureAlgorithm.gostr34102012_256, SignatureAlgorithm.gostr34102012_512,
+ SignatureAlgorithm.rsa_pss_pss_sha256, SignatureAlgorithm.rsa_pss_pss_sha384,
+ SignatureAlgorithm.rsa_pss_pss_sha512, SignatureAlgorithm.rsa_pss_rsae_sha256,
+ SignatureAlgorithm.rsa_pss_rsae_sha384, SignatureAlgorithm.rsa_pss_rsae_sha512, };
+
+ for (int i = 0; i < intrinsicSignatureAlgorithms.Length; ++i)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureAndHashAlgorithm.GetInstance(
+ HashAlgorithm.Intrinsic, intrinsicSignatureAlgorithms[i]);
+ if (!m_crypto.HasSignatureAndHashAlgorithm(signatureAndHashAlgorithm))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner12(cryptoParams,
+ signatureAndHashAlgorithm);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignature12(credentialedSigner, signatureAndHashAlgorithm);
+ }
+ }
+ }
+
+ [Test]
+ public void TestSignatures13()
+ {
+ int[] signatureSchemes = new int[] { SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256,
+ SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384, SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512,
+ SignatureScheme.ecdsa_secp256r1_sha256, SignatureScheme.ecdsa_secp384r1_sha384,
+ SignatureScheme.ecdsa_secp521r1_sha512, SignatureScheme.ed25519, SignatureScheme.ed448,
+ SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pss_pss_sha384, SignatureScheme.rsa_pss_pss_sha512,
+ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_rsae_sha384,
+ SignatureScheme.rsa_pss_rsae_sha512, SignatureScheme.sm2sig_sm3,
+ // These are only used for certs in 1.3 (cert verification is not done by TlsCrypto)
+ //SignatureScheme.ecdsa_sha1, SignatureScheme.rsa_pkcs1_sha1, SignatureScheme.rsa_pkcs1_sha256,
+ //SignatureScheme.rsa_pkcs1_sha384, SignatureScheme.rsa_pkcs1_sha512,
+ };
+
+ TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv13);
+
+ for (int i = 0; i < signatureSchemes.Length; ++i)
+ {
+ int signatureScheme = signatureSchemes[i];
+ if (!m_crypto.HasSignatureScheme(signatureScheme))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner13(cryptoParams, signatureScheme);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignature13(credentialedSigner, signatureScheme);
+ }
+ }
+ }
+
+ private byte[] CalculateHmac(int cryptoHashAlgorithm, TlsSecret hmacKey, byte[] hmacInput)
+ {
+ byte[] keyBytes = Extract(hmacKey);
+
+ TlsHmac hmac = m_crypto.CreateHmacForHash(cryptoHashAlgorithm);
+ hmac.SetKey(keyBytes, 0, keyBytes.Length);
+ hmac.Update(hmacInput, 0, hmacInput.Length);
+ return hmac.CalculateMac();
+ }
+
+ private void Expect(TlsSecret secret, string expectedHex)
+ {
+ Expect(Extract(secret), Hex(expectedHex));
+ }
+
+ private void Expect(byte[] actualOctets, string expectedHex)
+ {
+ Expect(actualOctets, Hex(expectedHex));
+ }
+
+ private void Expect(byte[] actualOctets, byte[] expectedOctets)
+ {
+ AssertArrayEquals(actualOctets, expectedOctets);
+ }
+
+ private byte[] Extract(TlsSecret secret)
+ {
+ return m_crypto.AdoptSecret(secret).Extract();
+ }
+
+ private byte[] GetCurrentHash(TlsHash hash)
+ {
+ return hash.CloneHash().CalculateHash();
+ }
+
+ private static void AssertArrayEquals(byte[] a, byte[] b)
+ {
+ Assert.IsTrue(Arrays.AreEqual(a, b));
+ }
+
+ private static byte[] Hex(string s)
+ {
+ return Utilities.Encoders.Hex.Decode(s.Replace(" ", ""));
+ }
+
+ private void ImplTestAgreement(TlsAgreement aA, TlsAgreement aB)
+ {
+ byte[] pA = aA.GenerateEphemeral();
+ byte[] pB = aB.GenerateEphemeral();
+
+ aA.ReceivePeerValue(pB);
+ aB.ReceivePeerValue(pA);
+
+ TlsSecret sA = aA.CalculateSecret();
+ TlsSecret sB = aB.CalculateSecret();
+
+ AssertArrayEquals(Extract(sA), Extract(sB));
+ }
+
+ private void ImplTestDHDomain(TlsDHConfig dhConfig)
+ {
+ int namedGroup = dhConfig.NamedGroup;
+ int bits = namedGroup >= 0
+ ? NamedGroup.GetFiniteFieldBits(namedGroup)
+ : dhConfig.ExplicitGroup.P.BitLength;
+
+ int rounds = System.Math.Max(2, 11 - (bits >> 10));
+
+ TlsDHDomain d = m_crypto.CreateDHDomain(dhConfig);
+
+ for (int i = 0; i < rounds; ++i)
+ {
+ TlsAgreement aA = d.CreateDH();
+ TlsAgreement aB = d.CreateDH();
+
+ ImplTestAgreement(aA, aB);
+ }
+ }
+
+ private void ImplTestECDomain(TlsECConfig ecConfig)
+ {
+ int bits = NamedGroup.GetCurveBits(ecConfig.NamedGroup);
+ int rounds = System.Math.Max(2, 12 - (bits >> 6));
+
+ TlsECDomain d = m_crypto.CreateECDomain(ecConfig);
+
+ for (int i = 0; i < rounds; ++i)
+ {
+ TlsAgreement aA = d.CreateECDH();
+ TlsAgreement aB = d.CreateECDH();
+
+ ImplTestAgreement(aA, aB);
+ }
+ }
+
+ private void ImplTestSignatureLegacy(TlsCredentialedSigner credentialedSigner)
+ {
+ byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100);
+
+ byte[] signature;
+ TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner();
+ if (null != tlsStreamSigner)
+ {
+ Stream output = tlsStreamSigner.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ signature = tlsStreamSigner.GetSignature();
+ }
+ else
+ {
+ TlsHash tlsHash = new CombinedHash(m_crypto);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(null, signature);
+
+ TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0);
+ TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(tlsCertificate.GetLegacySignatureAlgorithm());
+
+ bool verified;
+ TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned);
+ if (null != tlsStreamVerifier)
+ {
+ Stream output = tlsStreamVerifier.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ verified = tlsStreamVerifier.IsVerified();
+ }
+ else
+ {
+ TlsHash tlsHash = new CombinedHash(m_crypto);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ Assert.IsTrue(verified);
+ }
+
+ private void ImplTestSignature12(TlsCredentialedSigner credentialedSigner,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ short hashAlgorithm = signatureAndHashAlgorithm.Hash;
+
+ byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100);
+
+ byte[] signature;
+ TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner();
+ if (null != tlsStreamSigner)
+ {
+ Stream output = tlsStreamSigner.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ signature = tlsStreamSigner.GetSignature();
+ }
+ else
+ {
+ // Currently 1.2 relies on these being handled by stream signers
+ Assert.IsTrue(HashAlgorithm.Intrinsic != hashAlgorithm);
+
+ int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(signatureAndHashAlgorithm, signature);
+
+ TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0);
+ TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(signatureAndHashAlgorithm.Signature);
+
+ bool verified;
+ TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned);
+ if (null != tlsStreamVerifier)
+ {
+ Stream output = tlsStreamVerifier.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ verified = tlsStreamVerifier.IsVerified();
+ }
+ else
+ {
+ // Currently 1.2 relies on these being handled by stream verifiers
+ Assert.IsTrue(HashAlgorithm.Intrinsic != hashAlgorithm);
+
+ int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ Assert.IsTrue(verified);
+ }
+
+ private void ImplTestSignature13(TlsCredentialedSigner credentialedSigner, int signatureScheme)
+ {
+ byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100);
+
+ byte[] signature;
+ TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner();
+ if (null != tlsStreamSigner)
+ {
+ Stream output = tlsStreamSigner.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ signature = tlsStreamSigner.GetSignature();
+ }
+ else
+ {
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(
+ SignatureScheme.GetSignatureAndHashAlgorithm(signatureScheme), signature);
+
+ TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0);
+ TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(signatureScheme);
+
+ bool verified;
+ TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned);
+ if (null != tlsStreamVerifier)
+ {
+ Stream output = tlsStreamVerifier.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ verified = tlsStreamVerifier.IsVerified();
+ }
+ else
+ {
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ Assert.IsTrue(verified);
+ }
+
+ private class TestTlsCryptoParameters
+ : TlsCryptoParameters
+ {
+ private readonly ProtocolVersion m_serverVersion;
+
+ internal TestTlsCryptoParameters(ProtocolVersion serverVersion)
+ : base(null)
+ {
+ this.m_serverVersion = serverVersion;
+ }
+
+ public override ProtocolVersion ServerVersion
+ {
+ get { return m_serverVersion; }
+ }
+ }
+
+ private class TestTlsDHGroupVerifier
+ : DefaultTlsDHGroupVerifier
+ {
+ internal IList Groups
+ {
+ get { return m_groups; }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs b/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs
new file mode 100644
index 000000000..9edb3109b
--- /dev/null
+++ b/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs
@@ -0,0 +1,134 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class ByteQueueInputStreamTest
+ {
+ [Test]
+ public void TestAvailable()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+
+ // buffer is empty
+ Assert.AreEqual(0, input.Available);
+
+ // after adding once
+ input.AddBytes(new byte[10]);
+ Assert.AreEqual(10, input.Available);
+
+ // after adding more than once
+ input.AddBytes(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], 0, 4);
+ Assert.AreEqual(10, input.Available);
+
+ input.Close();
+ }
+
+ [Test]
+ public void TestSkip()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+
+ // skip when buffer is empty
+ Assert.AreEqual(0, input.Skip(10));
+
+ // skip equal to available
+ input.AddBytes(new byte[2]);
+ Assert.AreEqual(2, input.Skip(2));
+ Assert.AreEqual(0, input.Available);
+
+ // skip less than available
+ input.AddBytes(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();
+ }
+
+ [Test]
+ public void TestRead()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+ input.AddBytes(new byte[]{ 0x01, 0x02 });
+ input.AddBytes(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();
+ }
+
+ [Test]
+ public void TestReadArray()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+ input.AddBytes(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, 0, buffer.Length));
+ AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+
+ // read more than available
+ input.AddBytes(new byte[]{ 0x01, 0x02, 0x03 });
+ Assert.AreEqual(3, input.Read(buffer, 0, buffer.Length));
+ AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer);
+
+ input.Close();
+ }
+
+ [Test]
+ public void TestPeek()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+
+ 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.AddBytes(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();
+ }
+
+ private static void AssertArrayEquals(byte[] a, byte[] b)
+ {
+ Assert.IsTrue(Arrays.AreEqual(a, b));
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/CertChainUtilities.cs b/crypto/test/src/tls/test/CertChainUtilities.cs
new file mode 100644
index 000000000..8df8e48bc
--- /dev/null
+++ b/crypto/test/src/tls/test/CertChainUtilities.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Threading;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class CertChainUtilities
+ {
+ private static readonly SecureRandom Random = new SecureRandom();
+
+ private static long serialNumber = 1L;
+
+ /// <summary>We generate the CA's certificate.</summary>
+ public static X509Certificate CreateMasterCert(string rootDN, AsymmetricCipherKeyPair keyPair)
+ {
+ //
+ // create the certificate - version 1
+ //
+ X509V1CertificateGenerator gen = new X509V1CertificateGenerator();
+ gen.SetIssuerDN(new X509Name(rootDN));
+ gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber)));
+ gen.SetNotBefore(DateTime.UtcNow.AddDays(-30));
+ gen.SetNotAfter(DateTime.UtcNow.AddDays(30));
+ gen.SetSubjectDN(new X509Name(rootDN));
+ gen.SetPublicKey(keyPair.Public);
+
+ return SignV1(gen, keyPair.Private);
+ }
+
+ /// <summary>We generate an intermediate certificate signed by our CA.</summary>
+ public static X509Certificate CreateIntermediateCert(string interDN, AsymmetricKeyParameter pubKey,
+ AsymmetricKeyParameter caPrivKey, X509Certificate caCert)
+ {
+ //
+ // create the certificate - version 3
+ //
+ X509V3CertificateGenerator gen = new X509V3CertificateGenerator();
+ gen.SetIssuerDN(caCert.SubjectDN);
+ gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber)));
+ gen.SetNotBefore(DateTime.UtcNow.AddDays(-30));
+ gen.SetNotAfter(DateTime.UtcNow.AddDays(30));
+ gen.SetSubjectDN(new X509Name(interDN));
+ gen.SetPublicKey(pubKey);
+
+ //
+ // extensions
+ //
+ gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
+ new SubjectKeyIdentifierStructure(pubKey));
+ gen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ new AuthorityKeyIdentifierStructure(caCert));
+ gen.AddExtension(X509Extensions.BasicConstraints, true,
+ new BasicConstraints(0));
+
+ return SignV3(gen, caPrivKey);
+ }
+
+ /// <summary>We generate a certificate signed by our CA's intermediate certificate.</summary>
+ public static X509Certificate CreateEndEntityCert(string endEntityDN, AsymmetricKeyParameter pubKey,
+ AsymmetricKeyParameter caPrivKey, X509Certificate caCert)
+ {
+ X509V3CertificateGenerator gen = CreateBaseEndEntityGenerator(endEntityDN, pubKey, caCert);
+
+ return SignV3(gen, caPrivKey);
+ }
+
+ /// <summary>We generate a certificate signed by our CA's intermediate certificate with ExtendedKeyUsage
+ /// extension.</summary>
+ public static X509Certificate CreateEndEntityCert(string endEntityDN, AsymmetricKeyParameter pubKey,
+ AsymmetricKeyParameter caPrivKey, X509Certificate caCert, KeyPurposeID keyPurposeID)
+ {
+ X509V3CertificateGenerator gen = CreateBaseEndEntityGenerator(endEntityDN, pubKey, caCert);
+
+ gen.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(keyPurposeID));
+
+ return SignV3(gen, caPrivKey);
+ }
+
+ private static X509V3CertificateGenerator CreateBaseEndEntityGenerator(string endEntityDN,
+ AsymmetricKeyParameter pubKey, X509Certificate caCert)
+ {
+ //
+ // create the certificate - version 3
+ //
+ X509V3CertificateGenerator gen = new X509V3CertificateGenerator();
+ gen.SetIssuerDN(caCert.SubjectDN);
+ gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber)));
+ gen.SetNotBefore(DateTime.UtcNow.AddDays(-30));
+ gen.SetNotAfter(DateTime.UtcNow.AddDays(30));
+ gen.SetSubjectDN(new X509Name(endEntityDN));
+ gen.SetPublicKey(pubKey);
+
+ //
+ // add the extensions
+ //
+ gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
+ new SubjectKeyIdentifierStructure(pubKey));
+ gen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ new AuthorityKeyIdentifierStructure(caCert.GetPublicKey()));
+ gen.AddExtension(X509Extensions.BasicConstraints, true,
+ new BasicConstraints(false));
+
+ return gen;
+ }
+
+ private static X509Certificate SignV1(X509V1CertificateGenerator gen, AsymmetricKeyParameter caPrivKey)
+ {
+ return gen.Generate(new Asn1SignatureFactory("SHA256withRSA", caPrivKey, Random));
+ }
+
+ private static X509Certificate SignV3(X509V3CertificateGenerator gen, AsymmetricKeyParameter caPrivKey)
+ {
+ return gen.Generate(new Asn1SignatureFactory("SHA256withRSA", caPrivKey, Random));
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsProtocolTest.cs b/crypto/test/src/tls/test/DtlsProtocolTest.cs
new file mode 100644
index 000000000..388003666
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class DtlsProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+ DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+ 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 m_serverProtocol;
+ private readonly DatagramTransport m_serverTransport;
+ private volatile bool m_isShutdown = false;
+
+ internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_serverTransport = serverTransport;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockDtlsServer server = new MockDtlsServer();
+ DtlsTransport dtlsServer = m_serverProtocol.Accept(server, m_serverTransport);
+ byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+ while (!m_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);
+ Console.Error.Flush();
+ }
+ }
+
+ internal void Shutdown(Thread serverThread)
+ {
+ if (!m_isShutdown)
+ {
+ this.m_isShutdown = true;
+ serverThread.Join();
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsPskProtocolTest.cs b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
new file mode 100644
index 000000000..0da6cb661
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class DlsPskProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+ DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+ 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);
+
+ MockPskDtlsClient client = new MockPskDtlsClient(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 m_serverProtocol;
+ private readonly DatagramTransport m_serverTransport;
+ private volatile bool m_isShutdown = false;
+
+ internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_serverTransport = serverTransport;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockPskDtlsServer server = new MockPskDtlsServer();
+ DtlsTransport dtlsServer = m_serverProtocol.Accept(server, m_serverTransport);
+ byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+ while (!m_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);
+ Console.Error.Flush();
+ }
+ }
+
+ internal void Shutdown(Thread serverThread)
+ {
+ if (!m_isShutdown)
+ {
+ this.m_isShutdown = true;
+ serverThread.Join();
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestCase.cs b/crypto/test/src/tls/test/DtlsTestCase.cs
new file mode 100644
index 000000000..d93f17c27
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestCase.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class DtlsTestCase
+ {
+ private static void CheckDtlsVersions(ProtocolVersion[] versions)
+ {
+ if (versions != null)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ if (!versions[i].IsDtls)
+ throw new InvalidOperationException("Non-DTLS version");
+ }
+ }
+ }
+
+ [Test, TestCaseSource(typeof(DtlsTestSuite), "Suite")]
+ public void RunTest(TlsTestConfig config)
+ {
+ CheckDtlsVersions(config.clientSupportedVersions);
+ CheckDtlsVersions(config.serverSupportedVersions);
+
+ DtlsTestClientProtocol clientProtocol = new DtlsTestClientProtocol(config);
+ DtlsTestServerProtocol serverProtocol = new DtlsTestServerProtocol(config);
+
+ 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.Caught, "Unexpected server exception");
+ }
+ }
+
+ protected void LogException(Exception e)
+ {
+ if (TlsTestConfig.Debug)
+ {
+ Console.Error.WriteLine(e);
+ Console.Error.Flush();
+ }
+ }
+
+ internal class Server
+ {
+ private readonly DtlsTestCase m_outer;
+ private readonly DtlsTestServerProtocol m_serverProtocol;
+ private readonly DatagramTransport m_serverTransport;
+ private readonly TlsTestServerImpl m_serverImpl;
+
+ private volatile bool m_isShutdown = false;
+ private Exception m_caught = null;
+
+ internal Server(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol,
+ DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+ {
+ this.m_outer = outer;
+ this.m_serverProtocol = serverProtocol;
+ this.m_serverTransport = serverTransport;
+ this.m_serverImpl = serverImpl;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ DtlsTransport dtlsServer = m_serverProtocol.Accept(m_serverImpl, m_serverTransport);
+ byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+ while (!m_isShutdown)
+ {
+ int length = dtlsServer.Receive(buf, 0, buf.Length, 100);
+ if (length >= 0)
+ {
+ dtlsServer.Send(buf, 0, length);
+ }
+ }
+ dtlsServer.Close();
+ }
+ catch (Exception e)
+ {
+ this.m_caught = e;
+ m_outer.LogException(m_caught);
+ }
+ }
+
+ internal void Shutdown(Thread serverThread)
+ {
+ if (!m_isShutdown)
+ {
+ this.m_isShutdown = true;
+ //serverThread.Interrupt();
+ serverThread.Join();
+ }
+ }
+
+ internal Exception Caught
+ {
+ get { return m_caught; }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestClientProtocol.cs b/crypto/test/src/tls/test/DtlsTestClientProtocol.cs
new file mode 100644
index 000000000..99a7fa07b
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestClientProtocol.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class DtlsTestClientProtocol
+ : DtlsClientProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ public DtlsTestClientProtocol(TlsTestConfig config)
+ : base()
+ {
+ this.m_config = config;
+ }
+
+ protected override byte[] GenerateCertificateVerify(ClientHandshakeState state,
+ DigitallySigned certificateVerify)
+ {
+ if (certificateVerify.Algorithm != null && m_config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(m_config.clientAuthSigAlgClaimed, certificateVerify.Signature);
+ }
+
+ return base.GenerateCertificateVerify(state, certificateVerify);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestServerProtocol.cs b/crypto/test/src/tls/test/DtlsTestServerProtocol.cs
new file mode 100644
index 000000000..08fc2b6a9
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestServerProtocol.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class DtlsTestServerProtocol
+ : DtlsServerProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ public DtlsTestServerProtocol(TlsTestConfig config)
+ : base()
+ {
+ this.m_config = config;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestSuite.cs b/crypto/test/src/tls/test/DtlsTestSuite.cs
new file mode 100644
index 000000000..0af2be32c
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestSuite.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.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;
+
+ AddTestCase(testSuite, c, "FallbackGood");
+ }
+
+ /*
+ * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+ * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+ * alerts being raised
+ */
+
+#if false
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+ c.clientFallback = true;
+ c.clientSupportedVersions = ProtocolVersion.DTLSv10.Only();
+ c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+ AddTestCase(testSuite, c, "FallbackBad");
+ }
+#endif
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+ c.clientSupportedVersions = ProtocolVersion.DTLSv10.Only();
+
+ AddTestCase(testSuite, c, "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
+ */
+
+#if false
+ /*
+ * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is
+ * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+ c.ExpectClientFatalAlert(AlertDescription.internal_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is
+ * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server
+ * when it verifies the selected algorithm against the CertificateRequest supported
+ * algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.serverCheckSigAlgOfClientCerts = false;
+ c.ExpectServerFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends
+ * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the
+ * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms,
+ * we expect fatal alert to come from the server when it finds the claimed algorithm
+ * doesn't match the client certificate.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
+ SignatureAlgorithm.ecdsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+ c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+ c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadClientCertificate");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+ c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+ AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined");
+ }
+
+ /*
+ * Server sends SHA-256/RSA certificate, which is not the default {sha1,rsa} implied by the
+ * absent signature_algorithms extension. We expect fatal alert from the client when it
+ * verifies the certificate's 'signatureAlgorithm' against the implicit default signature_algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientSendSignatureAlgorithms = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.certificate_unknown);
+
+ AddTestCase(testSuite, c, prefix + "BadServerCertSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not in the default
+ * supported signature algorithms that the client sent. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the supported algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not the default {sha1,rsa}
+ * implied by the absent signature_algorithms extension. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the implicit default.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientCheckSigAlgOfServerCerts = false;
+ c.clientSendSignatureAlgorithms = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg2");
+ }
+#endif
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+
+ AddTestCase(testSuite, c, prefix + "GoodDefault");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodNoCertReq");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined");
+ }
+
+#if false
+ /*
+ * Server generates downgraded (RFC 8446) ServerHello. We expect fatal alert
+ * (illegal_parameter) from the client.
+ */
+ if (!TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.serverNegotiateVersion = version;
+ c.serverSupportedVersions = ProtocolVersion.DTLSv12.DownTo(version);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadDowngrade");
+ }
+#endif
+ }
+
+ private static void AddTestCase(IList testSuite, TlsTestConfig config, string name)
+ {
+ testSuite.Add(new TestCaseData(config).SetName(name));
+ }
+
+ private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion serverMaxVersion)
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientSupportedVersions = ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10);
+ c.serverSupportedVersions = serverMaxVersion.DownTo(ProtocolVersion.DTLSv10);
+ return c;
+ }
+
+ public static void RunTests()
+ {
+ foreach (TestCaseData data in Suite())
+ {
+ Console.WriteLine(data.TestName);
+ new DtlsTestCase().RunTest((TlsTestConfig)data.Arguments[0]);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/tls/test/LoggingDatagramTransport.cs
new file mode 100644
index 000000000..f675b72fc
--- /dev/null
+++ b/crypto/test/src/tls/test/LoggingDatagramTransport.cs
@@ -0,0 +1,87 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class LoggingDatagramTransport
+ : DatagramTransport
+ {
+ private static readonly string HEX_CHARS = "0123456789ABCDEF";
+
+ private readonly DatagramTransport m_transport;
+ private readonly TextWriter m_output;
+ private readonly long m_launchTimestamp;
+
+ public LoggingDatagramTransport(DatagramTransport transport, TextWriter output)
+ {
+ this.m_transport = transport;
+ this.m_output = output;
+ this.m_launchTimestamp = DateTimeUtilities.CurrentUnixMs();
+ }
+
+ public virtual int GetReceiveLimit()
+ {
+ return m_transport.GetReceiveLimit();
+ }
+
+ public virtual int GetSendLimit()
+ {
+ return m_transport.GetSendLimit();
+ }
+
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ int length = m_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);
+ m_transport.Send(buf, off, len);
+ }
+
+ public virtual void Close()
+ {
+ m_transport.Close();
+ }
+
+ private void DumpDatagram(string verb, byte[] buf, int off, int len)
+ {
+ long timestamp = DateTimeUtilities.CurrentUnixMs() - m_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) m_output.WriteLine(s);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockDatagramAssociation.cs b/crypto/test/src/tls/test/MockDatagramAssociation.cs
new file mode 100644
index 000000000..3e0c0f52b
--- /dev/null
+++ b/crypto/test/src/tls/test/MockDatagramAssociation.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections;
+using System.Threading;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class MockDatagramAssociation
+ {
+ private int m_mtu;
+ private MockDatagramTransport m_client, m_server;
+
+ public MockDatagramAssociation(int mtu)
+ {
+ this.m_mtu = mtu;
+
+ IList clientQueue = new ArrayList();
+ IList serverQueue = new ArrayList();
+
+ this.m_client = new MockDatagramTransport(this, clientQueue, serverQueue);
+ this.m_server = new MockDatagramTransport(this, serverQueue, clientQueue);
+ }
+
+ public virtual DatagramTransport Client
+ {
+ get { return m_client; }
+ }
+
+ public virtual DatagramTransport Server
+ {
+ get { return m_server; }
+ }
+
+ private class MockDatagramTransport
+ : DatagramTransport
+ {
+ private readonly MockDatagramAssociation m_outer;
+ private IList m_receiveQueue, m_sendQueue;
+
+ internal MockDatagramTransport(MockDatagramAssociation outer, IList receiveQueue, IList sendQueue)
+ {
+ this.m_outer = outer;
+ this.m_receiveQueue = receiveQueue;
+ this.m_sendQueue = sendQueue;
+ }
+
+ public virtual int GetReceiveLimit()
+ {
+ return m_outer.m_mtu;
+ }
+
+ public virtual int GetSendLimit()
+ {
+ return m_outer.m_mtu;
+ }
+
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ lock (m_receiveQueue)
+ {
+ if (m_receiveQueue.Count < 1)
+ {
+ try
+ {
+ Monitor.Wait(m_receiveQueue, waitMillis);
+ }
+ catch (ThreadInterruptedException)
+ {
+ // TODO Keep waiting until full wait expired?
+ }
+
+ if (m_receiveQueue.Count < 1)
+ return -1;
+ }
+
+ byte[] packet = (byte[])m_receiveQueue[0];
+ m_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 > m_outer.m_mtu)
+ {
+ // TODO Simulate rejection?
+ }
+
+ byte[] packet = Arrays.CopyOfRange(buf, off, off + len);
+
+ lock (m_sendQueue)
+ {
+ m_sendQueue.Add(packet);
+ Monitor.PulseAll(m_sendQueue);
+ }
+ }
+
+ public virtual void Close()
+ {
+ // TODO?
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockDtlsClient.cs b/crypto/test/src/tls/test/MockDtlsClient.cs
new file mode 100644
index 000000000..5aa1ebbd3
--- /dev/null
+++ b/crypto/test/src/tls/test/MockDtlsClient.cs
@@ -0,0 +1,170 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockDtlsClient
+ : DefaultTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockDtlsClient(TlsSession session)
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ this.m_session = session;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return this.m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("DTLS client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10);
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("DTLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new String[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem",
+ "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem",
+ "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem",
+ "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ short[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+
+ return TlsTestUtilities.LoadSignerCredentials(m_context,
+ certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-client-rsa.pem",
+ "x509-client-key-rsa.pem");
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockDtlsServer.cs b/crypto/test/src/tls/test/MockDtlsServer.cs
new file mode 100644
index 000000000..18e53628e
--- /dev/null
+++ b/crypto/test/src/tls/test/MockDtlsServer.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockDtlsServer
+ : DefaultTlsServer
+ {
+ internal MockDtlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("DTLS server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override CertificateRequest GetCertificateRequest()
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ IList serverSigAlgs = null;
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+ {
+ serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+
+ IList certificateAuthorities = new ArrayList();
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject);
+
+ // All the CA certificates are currently configured with this subject
+ certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA"));
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public override void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ TlsCertificate[] 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 = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+ if (isEmpty)
+ return;
+
+ string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem",
+ "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem",
+ "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem",
+ "x509-client-rsa.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa);
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskDtlsClient.cs b/crypto/test/src/tls/test/MockPskDtlsClient.cs
new file mode 100644
index 000000000..c83c9e7fd
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskDtlsClient.cs
@@ -0,0 +1,161 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskDtlsClient
+ : PskTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockPskDtlsClient(TlsSession session)
+ : this(session, new BasicTlsPskIdentity("client", Strings.ToUtf8ByteArray("TLS_TEST_PSK")))
+ {
+ }
+
+ internal MockPskDtlsClient(TlsSession session, TlsPskIdentity pskIdentity)
+ : base(new BcTlsCrypto(new SecureRandom()), pskIdentity)
+ {
+ this.m_session = session;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-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(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-PSK client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("DTLS-PSK client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.Only();
+ }
+
+ internal class MyTlsAuthentication
+ : ServerOnlyTlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public override void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("DTLS-PSK client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[] { "x509-server-rsa-enc.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskDtlsServer.cs b/crypto/test/src/tls/test/MockPskDtlsServer.cs
new file mode 100644
index 000000000..bb084535a
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskDtlsServer.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskDtlsServer
+ : PskTlsServer
+ {
+ internal MockPskDtlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager())
+ {
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-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(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-PSK server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("DTLS-PSK server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+
+ byte[] pskIdentity = m_context.SecurityParameters.PskIdentity;
+ if (pskIdentity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(pskIdentity);
+ Console.WriteLine("DTLS-PSK server completed handshake for PSK identity: " + name);
+ }
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[] { "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.Only();
+ }
+
+ internal class MyIdentityManager
+ : TlsPskIdentityManager
+ {
+ public byte[] GetHint()
+ {
+ return Strings.ToUtf8ByteArray("hint");
+ }
+
+ public byte[] GetPsk(byte[] identity)
+ {
+ if (identity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(identity);
+ if (name.Equals("client"))
+ {
+ return Strings.ToUtf8ByteArray("TLS_TEST_PSK");
+ }
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskTlsClient.cs b/crypto/test/src/tls/test/MockPskTlsClient.cs
new file mode 100644
index 000000000..46774266b
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskTlsClient.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskTlsClient
+ : PskTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockPskTlsClient(TlsSession session)
+ : this(session, new BasicTlsPskIdentity("client", Strings.ToUtf8ByteArray("TLS_TEST_PSK")))
+ {
+ }
+
+ internal MockPskTlsClient(TlsSession session, TlsPskIdentity pskIdentity)
+ : base(new BcTlsCrypto(new SecureRandom()), pskIdentity)
+ {
+ this.m_session = session;
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_1_1);
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ return protocolNames;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short 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 IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+
+ {
+ /*
+ * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
+ */
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16));
+ TlsExtensionsUtilities.AddTruncatedHmacExtension(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(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.Only();
+ }
+
+ internal class MyTlsAuthentication
+ : ServerOnlyTlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public override void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("TLS-PSK client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[] { "x509-server-rsa-enc.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskTlsServer.cs b/crypto/test/src/tls/test/MockPskTlsServer.cs
new file mode 100644
index 000000000..743073b04
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskTlsServer.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskTlsServer
+ : PskTlsServer
+ {
+ internal MockPskTlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager())
+ {
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ protocolNames.Add(ProtocolName.Http_1_1);
+ return protocolNames;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short 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 ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("TLS-PSK server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+
+ byte[] pskIdentity = m_context.SecurityParameters.PskIdentity;
+ if (pskIdentity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(pskIdentity);
+ Console.WriteLine("TLS-PSK server completed handshake for PSK identity: " + name);
+ }
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[] { "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.Only();
+ }
+
+ internal class MyIdentityManager
+ : TlsPskIdentityManager
+ {
+ public byte[] GetHint()
+ {
+ return Strings.ToUtf8ByteArray("hint");
+ }
+
+ public byte[] GetPsk(byte[] identity)
+ {
+ if (identity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(identity);
+ if (name.Equals("client"))
+ {
+ return Strings.ToUtf8ByteArray("TLS_TEST_PSK");
+ }
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockSrpTlsClient.cs b/crypto/test/src/tls/test/MockSrpTlsClient.cs
new file mode 100644
index 000000000..3d2232893
--- /dev/null
+++ b/crypto/test/src/tls/test/MockSrpTlsClient.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockSrpTlsClient
+ : SrpTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockSrpTlsClient(TlsSession session, TlsSrpIdentity srpIdentity)
+ : base(new BcTlsCrypto(new SecureRandom()), srpIdentity)
+ {
+ this.m_session = session;
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_1_1);
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ return protocolNames;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short 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 IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+
+ {
+ /*
+ * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
+ */
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16));
+ TlsExtensionsUtilities.AddTruncatedHmacExtension(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(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.Only();
+ }
+
+ internal class MyTlsAuthentication
+ : ServerOnlyTlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public override void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("TLS-SRP client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[] { "x509-server-dsa.pem", "x509-server-rsa_pss_256.pem",
+ "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/MockSrpTlsServer.cs b/crypto/test/src/tls/test/MockSrpTlsServer.cs
new file mode 100644
index 000000000..725901811
--- /dev/null
+++ b/crypto/test/src/tls/test/MockSrpTlsServer.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockSrpTlsServer
+ : SrpTlsServer
+ {
+ internal static readonly Srp6Group TEST_GROUP = Tls.Crypto.Srp6StandardGroups.rfc5054_1024;
+ internal static readonly byte[] TEST_IDENTITY = Strings.ToUtf8ByteArray("client");
+ internal static readonly byte[] TEST_PASSWORD = Strings.ToUtf8ByteArray("password");
+ internal static readonly TlsSrpIdentity TEST_SRP_IDENTITY = new BasicTlsSrpIdentity(TEST_IDENTITY,
+ TEST_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 BcTlsCrypto(new SecureRandom()), new MyIdentityManager(new BcTlsCrypto(new SecureRandom())))
+ {
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ protocolNames.Add(ProtocolName.Http_1_1);
+ return protocolNames;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short 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 ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("TLS-SRP server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+
+ byte[] srpIdentity = m_context.SecurityParameters.SrpIdentity;
+ if (srpIdentity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(srpIdentity);
+ Console.WriteLine("TLS-SRP server completed handshake for SRP identity: " + name);
+ }
+ }
+
+ protected override TlsCredentialedSigner GetDsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.dsa);
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa);
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ internal class MyIdentityManager
+ : TlsSrpIdentityManager
+ {
+ protected SimulatedTlsSrpIdentityManager m_unknownIdentityManager;
+
+ internal MyIdentityManager(TlsCrypto crypto)
+ {
+ m_unknownIdentityManager = SimulatedTlsSrpIdentityManager.GetRfc5054Default(crypto, TEST_GROUP,
+ TEST_SEED_KEY);
+ }
+
+ public TlsSrpLoginParameters GetLoginParameters(byte[] identity)
+ {
+ if (Arrays.ConstantTimeAreEqual(TEST_IDENTITY, identity))
+ {
+ Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator();
+ verifierGenerator.Init(TEST_GROUP.N, TEST_GROUP.G, new Sha1Digest());
+
+ BigInteger verifier = verifierGenerator.GenerateVerifier(TEST_SALT, identity, TEST_PASSWORD);
+
+ TlsSrpConfig srpConfig = new TlsSrpConfig();
+ srpConfig.SetExplicitNG(new BigInteger[]{ TEST_GROUP.N, TEST_GROUP.G });
+
+ return new TlsSrpLoginParameters(identity, srpConfig, verifier, TEST_SALT);
+ }
+
+ return m_unknownIdentityManager.GetLoginParameters(identity);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockTlsClient.cs b/crypto/test/src/tls/test/MockTlsClient.cs
new file mode 100644
index 000000000..62b699590
--- /dev/null
+++ b/crypto/test/src/tls/test/MockTlsClient.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockTlsClient
+ : DefaultTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockTlsClient(TlsSession session)
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ this.m_session = session;
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_1_1);
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ return protocolNames;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+
+ {
+ /*
+ * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
+ */
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16));
+ 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(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem",
+ "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem",
+ "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem",
+ "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ short[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+
+ return TlsTestUtilities.LoadSignerCredentials(m_context,
+ certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-client-rsa.pem",
+ "x509-client-key-rsa.pem");
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/MockTlsServer.cs b/crypto/test/src/tls/test/MockTlsServer.cs
new file mode 100644
index 000000000..94d4c7dfd
--- /dev/null
+++ b/crypto/test/src/tls/test/MockTlsServer.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockTlsServer
+ : DefaultTlsServer
+ {
+ internal MockTlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ protocolNames.Add(ProtocolName.Http_1_1);
+ return protocolNames;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short 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(short alertLevel, short alertDescription)
+ {
+ 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();
+
+ Console.WriteLine("TLS server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override CertificateRequest GetCertificateRequest()
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ IList serverSigAlgs = null;
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+ {
+ serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+
+ IList certificateAuthorities = new ArrayList();
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject);
+
+ // All the CA certificates are currently configured with this subject
+ certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA"));
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public override void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ TlsCertificate[] 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 = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+ if (isEmpty)
+ return;
+
+ string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem",
+ "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem",
+ "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem",
+ "x509-client-rsa.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa);
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/NetworkStream.cs b/crypto/test/src/tls/test/NetworkStream.cs
new file mode 100644
index 000000000..c5f1dfd59
--- /dev/null
+++ b/crypto/test/src/tls/test/NetworkStream.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class NetworkStream
+ : Stream
+ {
+ private readonly Stream m_inner;
+ private bool m_closed = false;
+
+ internal NetworkStream(Stream inner)
+ {
+ this.m_inner = inner;
+ }
+
+ internal virtual bool IsClosed
+ {
+ get { lock (this) return m_closed; }
+ }
+
+ public override bool CanRead
+ {
+ get { return m_inner.CanRead; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return m_inner.CanSeek; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return m_inner.CanWrite; }
+ }
+
+ public override void Close()
+ {
+ lock (this) m_closed = true;
+ }
+
+ public override void Flush()
+ {
+ m_inner.Flush();
+ }
+
+ public override long Length
+ {
+ get { return m_inner.Length; }
+ }
+
+ public override long Position
+ {
+ get { return m_inner.Position; }
+ set { m_inner.Position = value; }
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return m_inner.Seek(offset, origin);
+ }
+
+ public override void SetLength(long value)
+ {
+ m_inner.SetLength(value);
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ CheckNotClosed();
+ return m_inner.Read(buffer, offset, count);
+ }
+
+ public override int ReadByte()
+ {
+ CheckNotClosed();
+ return m_inner.ReadByte();
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ CheckNotClosed();
+ m_inner.Write(buf, off, len);
+ }
+
+ public override void WriteByte(byte value)
+ {
+ CheckNotClosed();
+ m_inner.WriteByte(value);
+ }
+
+ private void CheckNotClosed()
+ {
+ lock (this)
+ {
+ if (m_closed)
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PipedStream.cs b/crypto/test/src/tls/test/PipedStream.cs
new file mode 100644
index 000000000..6de5703e1
--- /dev/null
+++ b/crypto/test/src/tls/test/PipedStream.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+using System.Threading;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class PipedStream
+ : Stream
+ {
+ private readonly MemoryStream m_buf = new MemoryStream();
+ private bool m_closed = false;
+
+ private PipedStream m_other = null;
+ private long m_readPos = 0;
+
+ internal PipedStream()
+ {
+ }
+
+ internal PipedStream(PipedStream other)
+ {
+ lock (other)
+ {
+ this.m_other = other;
+ other.m_other = 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)
+ {
+ m_closed = 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 (m_other)
+ {
+ WaitForData();
+ int len = (int)System.Math.Min(count, m_other.m_buf.Position - m_readPos);
+ Array.Copy(m_other.m_buf.GetBuffer(), m_readPos, buffer, offset, len);
+ m_readPos += len;
+ return len;
+ }
+ }
+
+ public override int ReadByte()
+ {
+ lock (m_other)
+ {
+ WaitForData();
+ bool eof = (m_readPos >= m_other.m_buf.Position);
+ return eof ? -1 : m_other.m_buf.GetBuffer()[m_readPos++];
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ lock (this)
+ {
+ CheckOpen();
+ m_buf.Write(buf, off, len);
+ Monitor.PulseAll(this);
+ }
+ }
+
+ public override void WriteByte(byte value)
+ {
+ lock (this)
+ {
+ CheckOpen();
+ m_buf.WriteByte(value);
+ Monitor.PulseAll(m_buf);
+ }
+ }
+
+ private void CheckOpen()
+ {
+ if (m_closed)
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ private void WaitForData()
+ {
+ while (m_readPos >= m_other.m_buf.Position && !m_other.m_closed)
+ {
+ Monitor.Wait(m_other);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PrfTest.cs b/crypto/test/src/tls/test/PrfTest.cs
new file mode 100644
index 000000000..de00166ed
--- /dev/null
+++ b/crypto/test/src/tls/test/PrfTest.cs
@@ -0,0 +1,97 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class PrfTest
+ {
+ [Test]
+ public void TestLwTls11()
+ {
+ byte[] pre_master_secret = Hex.Decode("86051948e4d9a0cd273b6cd3a76557fc695e2ad9517cda97081ed009588a20ab48d0b128de8f917da74e711879460b60");
+ byte[] serverHello_random = Hex.Decode("55f1f273d4cdd4abb97f6856ed10f83a799dc42403c3f60c4e504419db4fd727");
+ byte[] clientHello_random = Hex.Decode("0b71e1f7232e675112510cf654a5e6280b3bd8ff078b67ec55276bfaddb92075");
+ byte[] server_random = Hex.Decode("a62615ee7fee41993588b2542735f90910c5a0f9c5dcb64898fdf3e90dc72a5f");
+ byte[] client_random = Hex.Decode("7798a130b732d7789e59a5fc14ad331ae91199f7d122e7fd4a594036b0694873");
+ byte[] master_secret = Hex.Decode("37841ef801f8cbdb49b6a164025de3e0ea8169604ffe80bd98b45cdd34105251cedac7223045ff4c7b67c8a12bf3141c");
+ byte[] key_block = Hex.Decode("c520e2409fa54facd3da01910f50a28f2f50986beb56b0c7b4cee9122e8f7428b7f7b8277bda931c71d35fdc2ea92127a5a143f63fe145275af5bcdab26113deffbb87a67f965b3964ea1ca29df1841c1708e6f42aacd87c12c4471913f61bb994fe3790b735dd11");
+
+ byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random);
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_legacy, ExporterLabel.master_secret, msSeed, master_secret.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong");
+
+ byte[] keSeed = Arrays.Concatenate(server_random, client_random);
+
+ TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_legacy, ExporterLabel.key_expansion, keSeed, key_block.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error");
+ }
+
+ [Test]
+ public void TestLwTls12_Sha256Prf()
+ {
+ byte[] pre_master_secret = Hex.Decode("f8938ecc9edebc5030c0c6a441e213cd24e6f770a50dda07876f8d55da062bcadb386b411fd4fe4313a604fce6c17fbc");
+ byte[] serverHello_random = Hex.Decode("f6c9575ed7ddd73e1f7d16eca115415812a43c2b747daaaae043abfb50053fce");
+ byte[] clientHello_random = Hex.Decode("36c129d01a3200894b9179faac589d9835d58775f9b5ea3587cb8fd0364cae8c");
+ byte[] server_random = Hex.Decode("ae6c806f8ad4d80784549dff28a4b58fd837681a51d928c3e30ee5ff14f39868");
+ byte[] client_random = Hex.Decode("62e1fd91f23f558a605f28478c58cf72637b89784d959df7e946d3f07bd1b616");
+ byte[] master_secret = Hex.Decode("202c88c00f84a17a20027079604787461176455539e705be730890602c289a5001e34eeb3a043e5d52a65e66125188bf");
+ byte[] key_block = Hex.Decode("d06139889fffac1e3a71865f504aa5d0d2a2e89506c6f2279b670c3e1b74f531016a2530c51a3a0f7e1d6590d0f0566b2f387f8d11fd4f731cdd572d2eae927f6f2f81410b25e6960be68985add6c38445ad9f8c64bf8068bf9a6679485d966f1ad6f68b43495b10a683755ea2b858d70ccac7ec8b053c6bd41ca299d4e51928");
+
+ byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random);
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha256, ExporterLabel.master_secret, msSeed, master_secret.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong");
+
+ byte[] keSeed = Arrays.Concatenate(server_random, client_random);
+
+ TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, keSeed, key_block.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error");
+ }
+
+ [Test]
+ public void TestLwTls12_Sha384Prf()
+ {
+ byte[] pre_master_secret = Hex.Decode("a5e2642633f5b8c81ad3fe0c2fe3a8e5ef806b06121dd10df4bb0fe857bfdcf522558e05d2682c9a80c741a3aab1716f");
+ byte[] serverHello_random = Hex.Decode("cb6e0b3eb02976b6466dfa9651c2919414f1648fd3a7838d02153e5bd39535b6");
+ byte[] clientHello_random = Hex.Decode("abe4bf5527429ac8eb13574d2709e8012bd1a113c6d3b1d3aa2c3840518778ac");
+ byte[] server_random = Hex.Decode("1b1c8568344a65c30828e7483c0e353e2c68641c9551efae6927d9cd627a107c");
+ byte[] client_random = Hex.Decode("954b5fe1849c2ede177438261f099a2fcd884d001b9fe1de754364b1f6a6dd8e");
+ byte[] master_secret = Hex.Decode("b4d49bfa87747fe815457bc3da15073d6ac73389e703079a3503c09e14bd559a5b3c7c601c7365f6ea8c68d3d9596827");
+ byte[] key_block = Hex.Decode("10fd89ef689c7ef033387b8a8f3e5e8e7c11f680f6bdd71fbac3246a73e98d45d03185dde686e6b2369e4503e9dc5a6d2cee3e2bf2fa3f41d3de57dff3e197c8a9d5f74cc2d277119d894f8584b07a0a5822f0bd68b3433ec6adaf5c9406c5f3ddbb71bbe17ce98f3d4d5893d3179ef369f57aad908e2bf710639100c3ce7e0c");
+
+ byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random);
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha384, ExporterLabel.master_secret, msSeed, master_secret.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong");
+
+ byte[] keSeed = Arrays.Concatenate(server_random, client_random);
+
+ TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha384, ExporterLabel.key_expansion, keSeed, key_block.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error");
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PskTlsClientTest.cs b/crypto/test/src/tls/test/PskTlsClientTest.cs
new file mode 100644
index 000000000..62cfc0dc2
--- /dev/null
+++ b/crypto/test/src/tls/test/PskTlsClientTest.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary>
+ /// <remarks>
+ /// 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.<br/<br/
+ /// In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys.
+ /// </remarks>
+ [TestFixture]
+ public class PskTlsClientTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ string host = "localhost";
+ int port = 5556;
+
+ long time1 = DateTimeUtilities.CurrentUnixMs();
+
+ /*
+ * 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 };
+
+ // These correspond to the configuration of MockPskTlsServer
+ string psk_identity = "client";
+ byte[] psk = Strings.ToUtf8ByteArray("TLS_TEST_PSK");
+
+ BasicTlsPskIdentity pskIdentity = new BasicTlsPskIdentity(psk_identity, psk);
+
+ MockPskTlsClient client = new MockPskTlsClient(null, pskIdentity);
+ TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client);
+ protocol.Close();
+
+ long time2 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 1: " + (time2 - time1) + "ms");
+
+ client = new MockPskTlsClient(client.GetSessionToResume(), pskIdentity);
+ protocol = OpenTlsClientConnection(host, port, client);
+
+ long time3 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 2: " + (time3 - time2) + "ms");
+
+ Http11Get(host, port, protocol.Stream);
+
+ protocol.Close();
+ }
+
+ private static void Http11Get(string host, int port, Stream s)
+ {
+ WriteUtf8Line(s, "GET / HTTP/1.1");
+ //WriteUtf8Line(s, "Host: " + host + ":" + port);
+ WriteUtf8Line(s, "");
+ s.Flush();
+
+ Console.WriteLine("---");
+
+ string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" };
+
+ StreamReader reader = new StreamReader(s);
+
+ bool finished = false;
+ string line;
+ while (!finished && (line = reader.ReadLine()) != null)
+ {
+ Console.WriteLine("<<< " + line);
+
+ string upperLine = TlsTestUtilities.ToUpperInvariant(line);
+
+ // TEST CODE ONLY. This is not a robust way of parsing the result!
+ foreach (string end in ends)
+ {
+ if (upperLine.Contains(end))
+ {
+ finished = true;
+ break;
+ }
+ }
+ }
+
+ Console.Out.Flush();
+ }
+
+ private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client)
+ {
+ TcpClient tcp = new TcpClient(hostname, port);
+
+ TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream());
+ protocol.Connect(client);
+ return protocol;
+ }
+
+ private static void WriteUtf8Line(Stream output, string line)
+ {
+ byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n");
+ output.Write(buf, 0, buf.Length);
+ Console.WriteLine(">>> " + line);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PskTlsServerTest.cs b/crypto/test/src/tls/test/PskTlsServerTest.cs
new file mode 100644
index 000000000..9d87a8d35
--- /dev/null
+++ b/crypto/test/src/tls/test/PskTlsServerTest.cs
@@ -0,0 +1,82 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ [TestFixture]
+ public class PskTlsServerTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ int port = 5556;
+
+ TcpListener ss = new TcpListener(IPAddress.Any, port);
+ ss.Start();
+ Stream stdout = Console.OpenStandardOutput();
+ try
+ {
+ while (true)
+ {
+ TcpClient s = ss.AcceptTcpClient();
+ Console.WriteLine("--------------------------------------------------------------------------------");
+ Console.WriteLine("Accepted " + s);
+ Server serverRun = new Server(s, stdout);
+ Thread t = new Thread(new ThreadStart(serverRun.Run));
+ t.Start();
+ }
+ }
+ finally
+ {
+ ss.Stop();
+ }
+ }
+
+ internal class Server
+ {
+ private readonly TcpClient s;
+ private readonly Stream stdout;
+
+ internal Server(TcpClient s, Stream stdout)
+ {
+ this.s = s;
+ this.stdout = stdout;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockPskTlsServer server = new MockPskTlsServer();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream());
+ serverProtocol.Accept(server);
+ Stream log = new TeeOutputStream(serverProtocol.Stream, stdout);
+ Streams.PipeAll(serverProtocol.Stream, log);
+ serverProtocol.Close();
+ }
+ finally
+ {
+ try
+ {
+ s.Close();
+ }
+ catch (IOException)
+ {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsClientTest.cs b/crypto/test/src/tls/test/TlsClientTest.cs
new file mode 100644
index 000000000..27e5f342b
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsClientTest.cs
@@ -0,0 +1,97 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ [TestFixture]
+ public class TlsClientTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ string host = "localhost";
+ int port = 5556;
+
+ long time1 = DateTimeUtilities.CurrentUnixMs();
+
+ MockTlsClient client = new MockTlsClient(null);
+ TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client);
+ protocol.Close();
+
+ long time2 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 1: " + (time2 - time1) + "ms");
+
+ client = new MockTlsClient(client.GetSessionToResume());
+ protocol = OpenTlsClientConnection(host, port, client);
+
+ long time3 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 2: " + (time3 - time2) + "ms");
+
+ Http11Get(host, port, protocol.Stream);
+
+ protocol.Close();
+ }
+
+ private static void Http11Get(string host, int port, Stream s)
+ {
+ WriteUtf8Line(s, "GET / HTTP/1.1");
+ //WriteUtf8Line(s, "Host: " + host + ":" + port);
+ WriteUtf8Line(s, "");
+ s.Flush();
+
+ Console.WriteLine("---");
+
+ string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" };
+
+ StreamReader reader = new StreamReader(s);
+
+ bool finished = false;
+ string line;
+ while (!finished && (line = reader.ReadLine()) != null)
+ {
+ Console.WriteLine("<<< " + line);
+
+ string upperLine = TlsTestUtilities.ToUpperInvariant(line);
+
+ // TEST CODE ONLY. This is not a robust way of parsing the result!
+ foreach (string end in ends)
+ {
+ if (upperLine.Contains(end))
+ {
+ finished = true;
+ break;
+ }
+ }
+ }
+
+ Console.Out.Flush();
+ }
+
+ private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client)
+ {
+ TcpClient tcp = new TcpClient(hostname, port);
+
+ TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream());
+ protocol.Connect(client);
+ return protocol;
+ }
+
+ private static void WriteUtf8Line(Stream output, string line)
+ {
+ byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n");
+ output.Write(buf, 0, buf.Length);
+ Console.WriteLine(">>> " + line);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs b/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs
new file mode 100644
index 000000000..56cd39e87
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs
@@ -0,0 +1,126 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsProtocolNonBlockingTest
+ {
+ [Test]
+ public void TestClientServerFragmented()
+ {
+ // tests if it's really non-blocking when partial records arrive
+ ImplTestClientServer(true);
+ }
+
+ [Test]
+ public void TestClientServerNonFragmented()
+ {
+ ImplTestClientServer(false);
+ }
+
+ private static void ImplTestClientServer(bool fragment)
+ {
+ TlsClientProtocol clientProtocol = new TlsClientProtocol();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol();
+
+ MockTlsClient client = new MockTlsClient(null);
+ MockTlsServer server = new MockTlsServer();
+
+ clientProtocol.Connect(client);
+ serverProtocol.Accept(server);
+
+ // 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];
+ client.Crypto.SecureRandom.NextBytes(data);
+
+ WriteAndRead(clientProtocol, serverProtocol, data, fragment);
+ WriteAndRead(serverProtocol, clientProtocol, data, fragment);
+
+ // close the connection
+ clientProtocol.Close();
+ PumpData(clientProtocol, serverProtocol, fragment);
+ serverProtocol.CloseInput();
+ CheckClosed(serverProtocol);
+ CheckClosed(clientProtocol);
+ }
+
+ private static void WriteAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, bool fragment)
+ {
+ int dataSize = data.Length;
+ writer.WriteApplicationData(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)
+ {
+ byte[] buffer = new byte[1];
+ while (from.GetAvailableOutputBytes() > 0)
+ {
+ 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.WriteApplicationData(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/tls/test/TlsProtocolTest.cs b/crypto/test/src/tls/test/TlsProtocolTest.cs
new file mode 100644
index 000000000..b4f79f9ba
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsProtocolTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+ MockTlsClient client = new MockTlsClient(null);
+ MockTlsServer server = new MockTlsServer();
+
+ Server serverRun = new Server(serverProtocol, server);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ clientProtocol.Connect(client);
+
+ byte[] data = new byte[1000];
+ client.Crypto.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 m_serverProtocol;
+ private readonly TlsServer m_server;
+
+ internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsPskProtocolTest.cs b/crypto/test/src/tls/test/TlsPskProtocolTest.cs
new file mode 100644
index 000000000..97e6f133b
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsPskProtocolTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsPskProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+ MockPskTlsClient client = new MockPskTlsClient(null);
+ MockPskTlsServer server = new MockPskTlsServer();
+
+ Server serverRun = new Server(serverProtocol, server);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ clientProtocol.Connect(client);
+
+ byte[] data = new byte[1000];
+ client.Crypto.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 m_serverProtocol;
+ private readonly TlsServer m_server;
+
+ internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsServerTest.cs b/crypto/test/src/tls/test/TlsServerTest.cs
new file mode 100644
index 000000000..333ff5664
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsServerTest.cs
@@ -0,0 +1,82 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ [TestFixture]
+ public class TlsServerTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ int port = 5556;
+
+ TcpListener ss = new TcpListener(IPAddress.Any, port);
+ ss.Start();
+ Stream stdout = Console.OpenStandardOutput();
+ try
+ {
+ while (true)
+ {
+ TcpClient s = ss.AcceptTcpClient();
+ Console.WriteLine("--------------------------------------------------------------------------------");
+ Console.WriteLine("Accepted " + s);
+ Server serverRun = new Server(s, stdout);
+ Thread t = new Thread(new ThreadStart(serverRun.Run));
+ t.Start();
+ }
+ }
+ finally
+ {
+ ss.Stop();
+ }
+ }
+
+ internal class Server
+ {
+ private readonly TcpClient s;
+ private readonly Stream stdout;
+
+ internal Server(TcpClient s, Stream stdout)
+ {
+ this.s = s;
+ this.stdout = stdout;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockTlsServer server = new MockTlsServer();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream());
+ serverProtocol.Accept(server);
+ Stream log = new TeeOutputStream(serverProtocol.Stream, stdout);
+ Streams.PipeAll(serverProtocol.Stream, log);
+ serverProtocol.Close();
+ }
+ finally
+ {
+ try
+ {
+ s.Close();
+ }
+ catch (IOException)
+ {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsSrpProtocolTest.cs b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
new file mode 100644
index 000000000..555b1f1f7
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsSrpProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+ MockSrpTlsClient client = new MockSrpTlsClient(null, MockSrpTlsServer.TEST_SRP_IDENTITY);
+ MockSrpTlsServer server = new MockSrpTlsServer();
+
+ Server serverRun = new Server(serverProtocol, server);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ clientProtocol.Connect(client);
+
+ byte[] data = new byte[1000];
+ client.Crypto.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 m_serverProtocol;
+ private readonly TlsServer m_server;
+
+ internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestCase.cs b/crypto/test/src/tls/test/TlsTestCase.cs
new file mode 100644
index 000000000..0489d22c1
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestCase.cs
@@ -0,0 +1,182 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsTestCase
+ {
+ private static void CheckTlsVersions(ProtocolVersion[] versions)
+ {
+ if (versions != null)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ if (!versions[i].IsTls)
+ throw new InvalidOperationException("Non-TLS version");
+ }
+ }
+ }
+
+ [Test, TestCaseSource(typeof(TlsTestSuite), "Suite")]
+ public void RunTest(TlsTestConfig config)
+ {
+ // Disable the test if it is not being run via TlsTestSuite
+ if (config == null)
+ return;
+
+ CheckTlsVersions(config.clientSupportedVersions);
+ CheckTlsVersions(config.serverSupportedVersions);
+
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ NetworkStream clientNet = new NetworkStream(clientPipe);
+ NetworkStream serverNet = new NetworkStream(serverPipe);
+
+ TlsTestClientProtocol clientProtocol = new TlsTestClientProtocol(clientNet, config);
+ TlsTestServerProtocol serverProtocol = new TlsTestServerProtocol(serverNet, config);
+
+ clientProtocol.IsResumableHandshake = true;
+ serverProtocol.IsResumableHandshake = true;
+
+ TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+ TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+ Server serverRun = new Server(this, serverProtocol, serverImpl);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ Exception caught = null;
+ try
+ {
+ clientProtocol.Connect(clientImpl);
+
+ byte[] data = new byte[1000];
+ clientImpl.Crypto.SecureRandom.NextBytes(data);
+
+ Stream stream = clientProtocol.Stream;
+ stream.Write(data, 0, data.Length);
+
+ byte[] echo = new byte[data.Length];
+ int count = Streams.ReadFully(stream, echo, 0, echo.Length);
+
+ Assert.AreEqual(count, data.Length);
+ Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+ Assert.IsTrue(Arrays.AreEqual(clientImpl.m_tlsServerEndPoint, serverImpl.m_tlsServerEndPoint));
+
+ if (!TlsUtilities.IsTlsV13(clientImpl.m_negotiatedVersion))
+ {
+ Assert.NotNull(clientImpl.m_tlsUnique);
+ Assert.NotNull(serverImpl.m_tlsUnique);
+ }
+ Assert.IsTrue(Arrays.AreEqual(clientImpl.m_tlsUnique, serverImpl.m_tlsUnique));
+
+ stream.Close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ LogException(caught);
+ }
+
+ serverRun.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(serverRun.m_caught, "Unexpected server exception");
+ }
+ }
+
+ protected virtual void LogException(Exception e)
+ {
+ if (TlsTestConfig.Debug)
+ {
+ Console.Error.WriteLine(e);
+ Console.Error.Flush();
+ }
+ }
+
+ internal class Server
+ {
+ protected readonly TlsTestCase m_outer;
+ protected readonly TlsServerProtocol m_serverProtocol;
+ protected readonly TlsServer m_server;
+
+ internal bool m_canExit = false;
+ internal Exception m_caught = null;
+
+ internal Server(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_outer = outer;
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ internal void AllowExit()
+ {
+ lock (this)
+ {
+ m_canExit = true;
+ Monitor.PulseAll(this);
+ }
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ m_caught = e;
+ m_outer.LogException(m_caught);
+ }
+
+ WaitExit();
+ }
+
+ protected void WaitExit()
+ {
+ lock (this)
+ {
+ while (!m_canExit)
+ {
+ try
+ {
+ Monitor.Wait(this);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestClientImpl.cs b/crypto/test/src/tls/test/TlsTestClientImpl.cs
new file mode 100644
index 000000000..d436df3f7
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestClientImpl.cs
@@ -0,0 +1,370 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestClientImpl
+ : DefaultTlsClient
+ {
+ private static readonly int[] TestCipherSuites = new int[]
+ {
+ /*
+ * TLS 1.3
+ */
+ CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_AES_128_GCM_SHA256,
+
+ /*
+ * pre-TLS 1.3
+ */
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ 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_CHACHA20_POLY1305_SHA256,
+ 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_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ 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,
+ };
+
+ protected readonly TlsTestConfig m_config;
+
+ protected int m_firstFatalAlertConnectionEnd = -1;
+ protected short m_firstFatalAlertDescription = -1;
+
+ internal ProtocolVersion m_negotiatedVersion = null;
+ internal byte[] m_tlsServerEndPoint = null;
+ internal byte[] m_tlsUnique = null;
+
+ internal TlsTestClientImpl(TlsTestConfig config)
+ : base(TlsTestSuite.GetCrypto(config))
+ {
+ this.m_config = config;
+ }
+
+ internal int FirstFatalAlertConnectionEnd
+ {
+ get { return m_firstFatalAlertConnectionEnd; }
+ }
+
+ internal short FirstFatalAlertDescription
+ {
+ get { return m_firstFatalAlertDescription; }
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = base.GetClientExtensions();
+ if (clientExtensions != null)
+ {
+ if (!m_config.clientSendSignatureAlgorithms)
+ {
+ clientExtensions.Remove(ExtensionType.signature_algorithms);
+ this.m_supportedSignatureAlgorithms = null;
+ }
+ if (!m_config.clientSendSignatureAlgorithmsCert)
+ {
+ clientExtensions.Remove(ExtensionType.signature_algorithms_cert);
+ this.m_supportedSignatureAlgorithmsCert = null;
+ }
+ }
+ return clientExtensions;
+ }
+
+ public override IList GetEarlyKeyShareGroups()
+ {
+ if (m_config.clientEmptyKeyShare)
+ return null;
+
+ return base.GetEarlyKeyShareGroups();
+ }
+
+ public override bool IsFallback()
+ {
+ return m_config.clientFallback;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ m_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(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ m_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 NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ m_tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ m_tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS client reports 'tls-server-end-point' = " + ToHexString(m_tlsServerEndPoint));
+ Console.WriteLine("TLS client reports 'tls-unique' = " + ToHexString(m_tlsUnique));
+ }
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ this.m_negotiatedVersion = serverVersion;
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS client negotiated " + serverVersion);
+ }
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(this, m_context);
+ }
+
+ protected virtual Certificate CorruptCertificate(Certificate cert)
+ {
+ CertificateEntry[] certEntryList = cert.GetCertificateEntryList();
+ CertificateEntry ee = certEntryList[0];
+ TlsCertificate corruptCert = CorruptCertificateSignature(ee.Certificate);
+ certEntryList[0] = new CertificateEntry(corruptCert, ee.Extensions);
+ return new Certificate(cert.GetCertificateRequestContext(), certEntryList);
+ }
+
+ protected virtual TlsCertificate CorruptCertificateSignature(TlsCertificate tlsCertificate)
+ {
+ X509CertificateStructure cert = X509CertificateStructure.GetInstance(tlsCertificate.GetEncoded());
+
+ Asn1EncodableVector v = new Asn1EncodableVector();
+ v.Add(cert.TbsCertificate);
+ v.Add(cert.SignatureAlgorithm);
+ v.Add(CorruptSignature(cert.Signature));
+
+ cert = X509CertificateStructure.GetInstance(new DerSequence(v));
+
+ return Crypto.CreateCertificate(cert.GetEncoded(Asn1Encodable.Der));
+ }
+
+ protected virtual DerBitString CorruptSignature(DerBitString bs)
+ {
+ return new DerBitString(CorruptBit(bs.GetOctets()));
+ }
+
+ protected virtual byte[] CorruptBit(byte[] bs)
+ {
+ bs = Arrays.Clone(bs);
+
+ // Flip a random bit
+ int bit = m_context.Crypto.SecureRandom.Next(bs.Length << 3);
+ bs[bit >> 3] ^= (byte)(1 << (bit & 7));
+
+ return bs;
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, TestCipherSuites);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ if (null != m_config.clientSupportedVersions)
+ {
+ return m_config.clientSupportedVersions;
+ }
+
+ return base.GetSupportedVersions();
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsTestClientImpl m_outer;
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsTestClientImpl outer, TlsContext context)
+ {
+ this.m_outer = outer;
+ this.m_context = context;
+ }
+
+ public virtual void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ 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 = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem",
+ "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem",
+ "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem",
+ "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ if (m_outer.m_config.clientCheckSigAlgOfServerCerts)
+ {
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ }
+
+ public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ TlsTestConfig config = m_outer.m_config;
+
+ if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ throw new InvalidOperationException();
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)
+ return null;
+
+ bool isTlsV13 = TlsUtilities.IsTlsV13(m_context);
+
+ if (!isTlsV13)
+ {
+ short[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+ }
+
+ IList supportedSigAlgs = certificateRequest.SupportedSignatureAlgorithms;
+ if (supportedSigAlgs != null && config.clientAuthSigAlg != null)
+ {
+ supportedSigAlgs = new ArrayList(1);
+ supportedSigAlgs.Add(config.clientAuthSigAlg);
+ }
+
+ // TODO[tls13] Check also supportedSigAlgsCert against the chain signature(s)
+
+ TlsCredentialedSigner signerCredentials = TlsTestUtilities.LoadSignerCredentials(m_context,
+ supportedSigAlgs, SignatureAlgorithm.rsa, "x509-client-rsa.pem", "x509-client-key-rsa.pem");
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
+ return signerCredentials;
+
+ return new MyTlsCredentialedSigner(m_outer, signerCredentials);
+ }
+ }
+
+ internal class MyTlsCredentialedSigner
+ : TlsCredentialedSigner
+ {
+ private readonly TlsTestClientImpl m_outer;
+ private readonly TlsCredentialedSigner m_inner;
+
+ internal MyTlsCredentialedSigner(TlsTestClientImpl outer, TlsCredentialedSigner inner)
+ {
+ this.m_outer = outer;
+ this.m_inner = inner;
+ }
+
+ public virtual byte[] GenerateRawSignature(byte[] hash)
+ {
+ byte[] sig = m_inner.GenerateRawSignature(hash);
+
+ if (m_outer.m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY)
+ {
+ sig = m_outer.CorruptBit(sig);
+ }
+
+ return sig;
+ }
+
+ public virtual Certificate Certificate
+ {
+ get
+ {
+ Certificate cert = m_inner.Certificate;
+
+ if (m_outer.m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT)
+ {
+ cert = m_outer.CorruptCertificate(cert);
+ }
+
+ return cert;
+ }
+ }
+
+ public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+ {
+ get { return m_inner.SignatureAndHashAlgorithm; }
+ }
+
+ public virtual TlsStreamSigner GetStreamSigner()
+ {
+ return null;
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestClientProtocol.cs b/crypto/test/src/tls/test/TlsTestClientProtocol.cs
new file mode 100644
index 000000000..f7e94680a
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestClientProtocol.cs
@@ -0,0 +1,32 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestClientProtocol
+ : TlsClientProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ internal TlsTestClientProtocol(Stream stream, TlsTestConfig config)
+ : this(stream, stream, config)
+ {
+ }
+
+ internal TlsTestClientProtocol(Stream input, Stream output, TlsTestConfig config)
+ : base(input, output)
+ {
+ this.m_config = config;
+ }
+
+ protected override void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
+ {
+ if (certificateVerify.Algorithm != null && m_config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(m_config.clientAuthSigAlgClaimed, certificateVerify.Signature);
+ }
+
+ base.SendCertificateVerifyMessage(certificateVerify);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestConfig.cs b/crypto/test/src/tls/test/TlsTestConfig.cs
new file mode 100644
index 000000000..a15d4e535
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestConfig.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class TlsTestConfig
+ {
+ // TODO[tls-port]
+ public static readonly bool Debug = true;
+
+ /// <summary>Client does not authenticate, ignores any certificate request.</summary>
+ public const int CLIENT_AUTH_NONE = 0;
+
+ /// <summary>Client will authenticate if it receives a certificate request.</summary>
+ public const int CLIENT_AUTH_VALID = 1;
+
+ /// <summary>Client will authenticate if it receives a certificate request, with an invalid certificate.
+ /// </summary>
+ public const int CLIENT_AUTH_INVALID_CERT = 2;
+
+ /// <summary>Client will authenticate if it receives a certificate request, with an invalid CertificateVerify
+ /// signature.</summary>
+ public const int CLIENT_AUTH_INVALID_VERIFY = 3;
+
+ public const int CRYPTO_BC = 0;
+
+ /// <summary>Server will not request a client certificate.</summary>
+ public const int SERVER_CERT_REQ_NONE = 0;
+
+ /// <summary>Server will request a client certificate but receiving one is optional.</summary>
+ public const int SERVER_CERT_REQ_OPTIONAL = 1;
+
+ /// <summary>Server will request a client certificate and receiving one is mandatory.</summary>
+ public const int SERVER_CERT_REQ_MANDATORY = 2;
+
+ /// <summary>Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants.
+ /// </summary>
+ public int clientAuth = CLIENT_AUTH_VALID;
+
+ /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be
+ /// used for the CertificateVerify signature(if one is sent).</summary>
+ public SignatureAndHashAlgorithm clientAuthSigAlg = null;
+
+ /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be
+ /// _claimed_ in the CertificateVerify (if one is sent), independently of what was actually used.</summary>
+ public SignatureAndHashAlgorithm clientAuthSigAlgClaimed = null;
+
+ /// <summary>Control whether the client will call
+ /// <see cref="TlsUtilities.CheckPeerSigAlgs(TlsContext, Crypto.TlsCertificate[])"/> to check the server
+ /// certificate chain.</summary>
+ public bool clientCheckSigAlgOfServerCerts = true;
+
+ public int clientCrypto = CRYPTO_BC;
+
+ /// <summary>Configures whether the client will send an empty key_share extension in initial ClientHello.
+ /// </summary>
+ public bool clientEmptyKeyShare = false;
+
+ /// <summary>Configures whether the client will indicate version fallback via TLS_FALLBACK_SCSV.</summary>
+ public bool clientFallback = false;
+
+ /// <summary>Configures whether a (TLS 1.2+) client may send the signature_algorithms extension in ClientHello.
+ /// </summary>
+ public bool clientSendSignatureAlgorithms = true;
+
+ /// <summary>Configures whether a (TLS 1.2+) client may send the signature_algorithms_cert extension in
+ /// ClientHello.</summary>
+ public bool clientSendSignatureAlgorithmsCert = true;
+
+ /// <summary>Configures the supported protocol versions for the client. If null, uses the library's default.
+ /// </summary>
+ public ProtocolVersion[] clientSupportedVersions = null;
+
+ /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be
+ /// used for the ServerKeyExchange signature(if one is sent).</summary>
+ public SignatureAndHashAlgorithm serverAuthSigAlg = null;
+
+ /// <summary>Configures whether the test server will send a certificate request.</summary>
+ public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
+
+ /// <summary>If TLS 1.2 or higher is negotiated, configures the set of supported signature algorithms in the
+ /// CertificateRequest (if one is sent). If null, uses a default set.</summary>
+ public IList serverCertReqSigAlgs = null;
+
+ /// <summary>Control whether the server will call
+ /// <see cref="TlsUtilities.CheckPeerSigAlgs(TlsContext, Crypto.TlsCertificate[])"/> to check the client
+ /// certificate chain.</summary>
+ public bool serverCheckSigAlgOfClientCerts = true;
+
+ public int serverCrypto = CRYPTO_BC;
+
+ /// <summary>Configures a protocol version the server will unconditionally negotiate.</summary>
+ /// <remarks>
+ /// Ignored if null.
+ /// </remarks>
+ public ProtocolVersion serverNegotiateVersion = null;
+
+ /// <summary>Configures the supported protocol versions for the server.</summary>
+ /// <remarks>
+ /// If null, uses the library's default.
+ /// </remarks>
+ public ProtocolVersion[] serverSupportedVersions = null;
+
+ /// <summary>Configures the connection end at which a fatal alert is expected to be raised.</summary>
+ /// <remarks>
+ /// Use <see cref="ConnectionEnd"/> constants.
+ /// </remarks>
+ public int expectFatalAlertConnectionEnd = -1;
+
+ /// <summary>Configures the type of fatal alert expected to be raised.</summary>
+ /// <remarks>
+ /// Use <see cref="AlertDescription"/> constants.
+ /// </remarks>
+ public short expectFatalAlertDescription = -1;
+
+ public virtual void ExpectClientFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.client;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+
+ public virtual void ExpectServerFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.server;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestServerImpl.cs b/crypto/test/src/tls/test/TlsTestServerImpl.cs
new file mode 100644
index 000000000..6bc4d315d
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestServerImpl.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestServerImpl
+ : DefaultTlsServer
+ {
+ private static readonly int[] TestCipherSuites = new int[]
+ {
+ /*
+ * TLS 1.3
+ */
+ CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_AES_256_GCM_SHA384,
+ CipherSuite.TLS_AES_128_GCM_SHA256,
+
+ /*
+ * pre-TLS 1.3
+ */
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ 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_CHACHA20_POLY1305_SHA256,
+ 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,
+ };
+
+ protected readonly TlsTestConfig m_config;
+
+ protected int m_firstFatalAlertConnectionEnd = -1;
+ protected short m_firstFatalAlertDescription = -1;
+
+ internal byte[] m_tlsServerEndPoint = null;
+ internal byte[] m_tlsUnique = null;
+
+ internal TlsTestServerImpl(TlsTestConfig config)
+ : base(TlsTestSuite.GetCrypto(config))
+ {
+ this.m_config = config;
+ }
+
+ internal int FirstFatalAlertConnectionEnd
+ {
+ get { return m_firstFatalAlertConnectionEnd; }
+ }
+
+ internal short FirstFatalAlertDescription
+ {
+ get { return m_firstFatalAlertDescription; }
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ /*
+ * TODO[tls13] Should really be finding the first client-supported signature scheme that the
+ * server also supports and has credentials for.
+ */
+ if (TlsUtilities.IsTlsV13(m_context))
+ {
+ return GetRsaSignerCredentials();
+ }
+
+ return base.GetCredentials();
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ m_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(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ m_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 void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ m_tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ m_tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS server reports 'tls-server-end-point' = " + ToHexString(m_tlsServerEndPoint));
+ Console.WriteLine("TLS server reports 'tls-unique' = " + ToHexString(m_tlsUnique));
+ }
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = (null != m_config.serverNegotiateVersion)
+ ? m_config.serverNegotiateVersion
+ : base.GetServerVersion();
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS server negotiated " + serverVersion);
+ }
+
+ return serverVersion;
+ }
+
+ public override CertificateRequest GetCertificateRequest()
+ {
+ if (m_config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ return null;
+
+ IList serverSigAlgs = null;
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+ {
+ serverSigAlgs = m_config.serverCertReqSigAlgs;
+ if (serverSigAlgs == null)
+ {
+ serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+ }
+
+ IList certificateAuthorities = new ArrayList();
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject);
+
+ // All the CA certificates are currently configured with this subject
+ certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA"));
+
+ if (TlsUtilities.IsTlsV13(m_context))
+ {
+ // TODO[tls13] Support for non-empty request context
+ byte[] certificateRequestContext = TlsUtilities.EmptyBytes;
+
+ // TODO[tls13] Add TlsTestConfig.serverCertReqSigAlgsCert
+ IList serverSigAlgsCert = null;
+
+ return new CertificateRequest(certificateRequestContext, serverSigAlgs, serverSigAlgsCert,
+ certificateAuthorities);
+ }
+ else
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+ }
+
+ public override void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+ if (isEmpty != (m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE))
+ throw new InvalidOperationException();
+
+ if (isEmpty && (m_config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY))
+ {
+ short alertDescription = TlsUtilities.IsTlsV13(m_context)
+ ? AlertDescription.certificate_required
+ : AlertDescription.handshake_failure;
+
+ throw new TlsFatalAlert(alertDescription);
+ }
+
+ TlsCertificate[] chain = clientCertificate.GetCertificateList();
+
+ 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 = X509CertificateStructure.GetInstance(chain[0].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+ }
+
+ if (isEmpty)
+ return;
+
+ string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem",
+ "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem",
+ "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem",
+ "x509-client-rsa.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ if (m_config.serverCheckSigAlgOfClientCerts)
+ {
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ }
+
+ protected virtual IList GetSupportedSignatureAlgorithms()
+ {
+ if (TlsUtilities.IsTlsV12(m_context) && m_config.serverAuthSigAlg != null)
+ {
+ IList signatureAlgorithms = new ArrayList(1);
+ signatureAlgorithms.Add(m_config.serverAuthSigAlg);
+ return signatureAlgorithms;
+ }
+
+ return m_context.SecurityParameters.ClientSigAlgs;
+ }
+
+ protected override TlsCredentialedSigner GetDsaSignerCredentials()
+ {
+ return LoadSignerCredentials(SignatureAlgorithm.dsa);
+ }
+
+ protected override TlsCredentialedSigner GetECDsaSignerCredentials()
+ {
+ // TODO[RFC 8422] Code should choose based on client's supported sig algs?
+ return LoadSignerCredentials(SignatureAlgorithm.ecdsa);
+ //return LoadSignerCredentials(SignatureAlgorithm.ed25519);
+ //return LoadSignerCredentials(SignatureAlgorithm.ed448);
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ return LoadSignerCredentials(SignatureAlgorithm.rsa);
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, TestCipherSuites);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ if (m_config.serverSupportedVersions != null)
+ {
+ return m_config.serverSupportedVersions;
+ }
+
+ return base.GetSupportedVersions();
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ private TlsCredentialedSigner LoadSignerCredentials(short signatureAlgorithm)
+ {
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, GetSupportedSignatureAlgorithms(),
+ signatureAlgorithm);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestServerProtocol.cs b/crypto/test/src/tls/test/TlsTestServerProtocol.cs
new file mode 100644
index 000000000..632f6b8bf
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestServerProtocol.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestServerProtocol
+ : TlsServerProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ internal TlsTestServerProtocol(Stream stream, TlsTestConfig config)
+ : this(stream, stream, config)
+ {
+ }
+
+ internal TlsTestServerProtocol(Stream input, Stream output, TlsTestConfig config)
+ : base(input, output)
+ {
+ this.m_config = config;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestSuite.cs b/crypto/test/src/tls/test/TlsTestSuite.cs
new file mode 100644
index 000000000..adedd8249
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestSuite.cs
@@ -0,0 +1,300 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class TlsTestSuite
+ {
+ internal static TlsCrypto BC_CRYPTO = new BcTlsCrypto(new SecureRandom());
+
+ internal static TlsCrypto GetCrypto(TlsTestConfig config)
+ {
+ switch (config.clientCrypto)
+ {
+ case TlsTestConfig.CRYPTO_BC:
+ default:
+ return BC_CRYPTO;
+ }
+ }
+
+ // Make the access to constants less verbose
+ internal abstract class C : TlsTestConfig {}
+
+ public TlsTestSuite()
+ {
+ }
+
+ public static IEnumerable Suite()
+ {
+ IList testSuite = new ArrayList();
+ AddAllTests(testSuite, TlsTestConfig.CRYPTO_BC, TlsTestConfig.CRYPTO_BC);
+ return testSuite;
+ }
+
+ private static void AddAllTests(IList testSuite, int clientCrypto, int serverCrypto)
+ {
+ AddFallbackTests(testSuite, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.SSLv3, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv10, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv11, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv13, clientCrypto, serverCrypto);
+ }
+
+ private static void AddFallbackTests(IList testSuite, int clientCrypto, int serverCrypto)
+ {
+ string prefix = GetCryptoName(clientCrypto) + "_" + GetCryptoName(serverCrypto) + "_";
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ c.clientFallback = true;
+
+ AddTestCase(testSuite, c, prefix + "FallbackGood");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ c.clientFallback = true;
+ c.clientSupportedVersions = ProtocolVersion.TLSv11.DownTo(ProtocolVersion.TLSv10);
+ c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+ AddTestCase(testSuite, c, prefix + "FallbackBad");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ c.clientSupportedVersions = ProtocolVersion.TLSv11.DownTo(ProtocolVersion.TLSv10);
+
+ AddTestCase(testSuite, c, prefix + "FallbackNone");
+ }
+ }
+
+ private static void AddVersionTests(IList testSuite, ProtocolVersion version, int clientCrypto,
+ int serverCrypto)
+ {
+ string prefix = GetCryptoName(clientCrypto) + "_" + GetCryptoName(serverCrypto) + "_"
+ + version.ToString().Replace(" ", "").Replace(".", "") + "_";
+
+ bool isTlsV12 = TlsUtilities.IsTlsV12(version);
+ bool isTlsV13 = TlsUtilities.IsTlsV13(version);
+ bool isTlsV12Exactly = isTlsV12 && !isTlsV13;
+
+ short certReqDeclinedAlert = TlsUtilities.IsTlsV13(version)
+ ? AlertDescription.certificate_required
+ : AlertDescription.handshake_failure;
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+
+ AddTestCase(testSuite, c, prefix + "GoodDefault");
+ }
+
+ if (isTlsV13)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientEmptyKeyShare = true;
+
+ AddTestCase(testSuite, c, prefix + "GoodEmptyKeyShare");
+ }
+
+ /*
+ * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is
+ * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client.
+ */
+ if (isTlsV12Exactly)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+ c.serverCheckSigAlgOfClientCerts = false;
+ c.ExpectClientFatalAlert(AlertDescription.internal_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is
+ * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server
+ * when it verifies the selected algorithm against the CertificateRequest supported
+ * algorithms.
+ */
+ if (isTlsV12)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.serverCheckSigAlgOfClientCerts = false;
+ c.ExpectServerFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends
+ * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the
+ * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms,
+ * we expect fatal alert to come from the server when it finds the claimed algorithm
+ * doesn't match the client certificate.
+ */
+ if (isTlsV12)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+ c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+ c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadClientCertificate");
+ }
+
+ if (isTlsV13)
+ {
+ /*
+ * For TLS 1.3 the supported_algorithms extension is required in ClientHello when the
+ * server authenticates via a certificate.
+ */
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientSendSignatureAlgorithms = false;
+ c.clientSendSignatureAlgorithmsCert = false;
+ c.ExpectServerFatalAlert(AlertDescription.missing_extension);
+
+ AddTestCase(testSuite, c, prefix + "BadClientSigAlgs");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+ c.ExpectServerFatalAlert(certReqDeclinedAlert);
+
+ AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined");
+ }
+
+ /*
+ * Server sends SHA-256/RSA certificate, which is not the default {sha1,rsa} implied by the
+ * absent signature_algorithms extension. We expect fatal alert from the client when it
+ * verifies the certificate's 'signatureAlgorithm' against the implicit default signature_algorithms.
+ */
+ if (isTlsV12Exactly)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientSendSignatureAlgorithms = false;
+ c.clientSendSignatureAlgorithmsCert = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadServerCertSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not in the default
+ * supported signature algorithms that the client sent. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the supported algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not the default {sha1,rsa}
+ * implied by the absent signature_algorithms extension. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the implicit default.
+ */
+ if (isTlsV12Exactly)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientCheckSigAlgOfServerCerts = false;
+ c.clientSendSignatureAlgorithms = false;
+ c.clientSendSignatureAlgorithmsCert = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg2");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodNoCertReq");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined");
+ }
+
+ /*
+ * Server generates downgraded (RFC 8446) 1.1 ServerHello. We expect fatal alert
+ * (illegal_parameter) from the client.
+ */
+ if (!isTlsV13)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.serverNegotiateVersion = version;
+ c.serverSupportedVersions = ProtocolVersion.TLSv13.DownTo(version);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadDowngrade");
+ }
+ }
+
+ private static void AddTestCase(IList testSuite, TlsTestConfig config, string name)
+ {
+ testSuite.Add(new TestCaseData(config).SetName(name));
+ }
+
+ private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion serverMaxVersion, int clientCrypto,
+ int serverCrypto)
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientCrypto = clientCrypto;
+ c.clientSupportedVersions = ProtocolVersion.TLSv13.DownTo(ProtocolVersion.SSLv3);
+ c.serverCrypto = serverCrypto;
+ c.serverSupportedVersions = serverMaxVersion.DownTo(ProtocolVersion.SSLv3);
+ return c;
+ }
+
+ private static string GetCryptoName(int crypto)
+ {
+ switch (crypto)
+ {
+ case TlsTestConfig.CRYPTO_BC:
+ default:
+ return "BC";
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestUtilities.cs b/crypto/test/src/tls/test/TlsTestUtilities.cs
new file mode 100644
index 000000000..3ecacb0f0
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestUtilities.cs
@@ -0,0 +1,412 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public 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 bool EqualsIgnoreCase(string a, string b)
+ {
+ return ToUpperInvariant(a) == ToUpperInvariant(b);
+ }
+
+ internal static string ToUpperInvariant(string s)
+ {
+ return s.ToUpper(CultureInfo.InvariantCulture);
+ }
+
+ internal static string Fingerprint(X509CertificateStructure c)
+ {
+ byte[] der = c.GetEncoded();
+ byte[] hash = Sha256DigestOf(der);
+ byte[] hexBytes = Hex.Encode(hash);
+ string hex = ToUpperInvariant(Encoding.ASCII.GetString(hexBytes));
+
+ 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 string GetCACertResource(short signatureAlgorithm)
+ {
+ return "x509-ca-" + GetResourceName(signatureAlgorithm) + ".pem";
+ }
+
+ internal static string GetCACertResource(string eeCertResource)
+ {
+ if (eeCertResource.StartsWith("x509-client-"))
+ {
+ eeCertResource = eeCertResource.Substring("x509-client-".Length);
+ }
+ if (eeCertResource.StartsWith("x509-server-"))
+ {
+ eeCertResource = eeCertResource.Substring("x509-server-".Length);
+ }
+ if (eeCertResource.EndsWith(".pem"))
+ {
+ eeCertResource = eeCertResource.Substring(0, eeCertResource.Length - ".pem".Length);
+ }
+
+ if (EqualsIgnoreCase("dsa", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.dsa);
+ }
+
+ if (EqualsIgnoreCase("ecdh", eeCertResource)
+ || EqualsIgnoreCase("ecdsa", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.ecdsa);
+ }
+
+ if (EqualsIgnoreCase("ed25519", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.ed25519);
+ }
+
+ if (EqualsIgnoreCase("ed448", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.ed448);
+ }
+
+ if (EqualsIgnoreCase("rsa", eeCertResource)
+ || EqualsIgnoreCase("rsa-enc", eeCertResource)
+ || EqualsIgnoreCase("rsa-sign", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa);
+ }
+
+ if (EqualsIgnoreCase("rsa_pss_256", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha256);
+ }
+ if (EqualsIgnoreCase("rsa_pss_384", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha384);
+ }
+ if (EqualsIgnoreCase("rsa_pss_512", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha512);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ internal static string GetResourceName(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return "rsa";
+ case SignatureAlgorithm.dsa:
+ return "dsa";
+ case SignatureAlgorithm.ecdsa:
+ return "ecdsa";
+ case SignatureAlgorithm.ed25519:
+ return "ed25519";
+ case SignatureAlgorithm.ed448:
+ return "ed448";
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ return "rsa_pss_256";
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ return "rsa_pss_384";
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return "rsa_pss_512";
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ internal static TlsCredentialedAgreement LoadAgreementCredentials(TlsContext context, string[] certResources,
+ string keyResource)
+ {
+ TlsCrypto crypto = context.Crypto;
+ Certificate certificate = LoadCertificateChain(context, certResources);
+
+ // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data)
+ if (crypto is BcTlsCrypto)
+ {
+ AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource);
+
+ return new BcDefaultTlsCredentialedAgreement((BcTlsCrypto)crypto, certificate, privateKey);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ internal static TlsCredentialedDecryptor LoadEncryptionCredentials(TlsContext context, string[] certResources,
+ string keyResource)
+ {
+ TlsCrypto crypto = context.Crypto;
+ Certificate certificate = LoadCertificateChain(context, certResources);
+
+ // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data)
+ if (crypto is BcTlsCrypto)
+ {
+ AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource);
+
+ return new BcDefaultTlsCredentialedDecryptor((BcTlsCrypto)crypto, certificate, privateKey);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public static TlsCredentialedSigner LoadSignerCredentials(TlsCryptoParameters cryptoParams, TlsCrypto crypto,
+ string[] certResources, string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ Certificate certificate = LoadCertificateChain(cryptoParams.ServerVersion, crypto, certResources);
+
+ // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data)
+ if (crypto is BcTlsCrypto)
+ {
+ AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource);
+
+ return new BcDefaultTlsCredentialedSigner(cryptoParams, (BcTlsCrypto)crypto, privateKey, certificate, signatureAndHashAlgorithm);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ internal static TlsCredentialedSigner LoadSignerCredentials(TlsContext context, string[] certResources,
+ string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ TlsCrypto crypto = context.Crypto;
+ TlsCryptoParameters cryptoParams = new TlsCryptoParameters(context);
+
+ return LoadSignerCredentials(cryptoParams, crypto, certResources, keyResource, signatureAndHashAlgorithm);
+ }
+
+ internal static TlsCredentialedSigner LoadSignerCredentials(TlsContext context,
+ IList supportedSignatureAlgorithms, short signatureAlgorithm, string certResource, string keyResource)
+ {
+ if (supportedSignatureAlgorithms == null)
+ {
+ supportedSignatureAlgorithms = TlsUtilities.GetDefaultSignatureAlgorithms(signatureAlgorithm);
+ }
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+
+ foreach (SignatureAndHashAlgorithm alg in supportedSignatureAlgorithms)
+ {
+ if (alg.Signature == signatureAlgorithm)
+ {
+ // Just grab the first one we find
+ signatureAndHashAlgorithm = alg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ return null;
+
+ return LoadSignerCredentials(context, new string[]{ certResource }, keyResource,
+ signatureAndHashAlgorithm);
+ }
+
+ internal static TlsCredentialedSigner LoadSignerCredentialsServer(TlsContext context,
+ IList supportedSignatureAlgorithms, short signatureAlgorithm)
+ {
+ string sigName = GetResourceName(signatureAlgorithm);
+
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ sigName += "-sign";
+ break;
+ }
+
+ string certResource = "x509-server-" + sigName + ".pem";
+ string keyResource = "x509-server-key-" + sigName + ".pem";
+
+ return LoadSignerCredentials(context, supportedSignatureAlgorithms, signatureAlgorithm, certResource,
+ keyResource);
+ }
+
+ internal static Certificate LoadCertificateChain(ProtocolVersion protocolVersion, TlsCrypto crypto,
+ string[] resources)
+ {
+ if (TlsUtilities.IsTlsV13(protocolVersion))
+ {
+ CertificateEntry[] certificateEntryList = new CertificateEntry[resources.Length];
+ for (int i = 0; i < resources.Length; ++i)
+ {
+ TlsCertificate certificate = LoadCertificateResource(crypto, resources[i]);
+
+ // TODO[tls13] Add possibility of specifying e.g. CertificateStatus
+ IDictionary extensions = null;
+
+ certificateEntryList[i] = new CertificateEntry(certificate, extensions);
+ }
+
+ // TODO[tls13] Support for non-empty request context
+ byte[] certificateRequestContext = TlsUtilities.EmptyBytes;
+
+ return new Certificate(certificateRequestContext, certificateEntryList);
+ }
+ else
+ {
+ TlsCertificate[] chain = new TlsCertificate[resources.Length];
+ for (int i = 0; i < resources.Length; ++i)
+ {
+ chain[i] = LoadCertificateResource(crypto, resources[i]);
+ }
+ return new Certificate(chain);
+ }
+ }
+
+ internal static Certificate LoadCertificateChain(TlsContext context, string[] resources)
+ {
+ return LoadCertificateChain(context.ServerVersion, context.Crypto, resources);
+ }
+
+ internal static X509CertificateStructure LoadBcCertificateResource(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 TlsCertificate LoadCertificateResource(TlsCrypto crypto, string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("CERTIFICATE"))
+ {
+ return crypto.CreateCertificate(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid certificate", "resource");
+ }
+
+ internal static AsymmetricKeyParameter LoadBcPrivateKeyResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.Equals("PRIVATE KEY"))
+ {
+ return PrivateKeyFactory.CreateKey(pem.Content);
+ }
+ if (pem.Type.Equals("ENCRYPTED PRIVATE KEY"))
+ {
+ throw new NotSupportedException("Encrypted PKCS#8 keys not supported");
+ }
+ if (pem.Type.Equals("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.Equals("EC PRIVATE KEY"))
+ {
+ ECPrivateKeyStructure pKey = ECPrivateKeyStructure.GetInstance(pem.Content);
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey,
+ pKey.GetParameters());
+ PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey);
+ return PrivateKeyFactory.CreateKey(privInfo);
+ }
+ 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;
+ }
+
+ internal static bool AreSameCertificate(TlsCrypto crypto, TlsCertificate cert, string resource)
+ {
+ // TODO Cache test resources?
+ return AreSameCertificate(cert, LoadCertificateResource(crypto, resource));
+ }
+
+ internal static bool AreSameCertificate(TlsCertificate a, TlsCertificate b)
+ {
+ // TODO[tls-ops] Support equals on TlsCertificate?
+ return Arrays.AreEqual(a.GetEncoded(), b.GetEncoded());
+ }
+
+ internal static TlsCertificate[] GetTrustedCertPath(TlsCrypto crypto, TlsCertificate cert, string[] resources)
+ {
+ foreach (string eeCertResource in resources)
+ {
+ TlsCertificate eeCert = LoadCertificateResource(crypto, eeCertResource);
+ if (AreSameCertificate(cert, eeCert))
+ {
+ string caCertResource = GetCACertResource(eeCertResource);
+ TlsCertificate caCert = LoadCertificateResource(crypto, caCertResource);
+ if (null != caCert)
+ {
+ return new TlsCertificate[]{ eeCert, caCert };
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsUtilitiesTest.cs b/crypto/test/src/tls/test/TlsUtilitiesTest.cs
new file mode 100644
index 000000000..702c40082
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsUtilitiesTest.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsUtilitiesTest
+ {
+ [Test]
+ public void TestChooseSignatureAndHash()
+ {
+ int keyExchangeAlgorithm = KeyExchangeAlgorithm.ECDHE_RSA;
+ short signatureAlgorithm = TlsUtilities.GetLegacySignatureAlgorithmServer(keyExchangeAlgorithm);
+
+ IList supportedSignatureAlgorithms = GetSignatureAlgorithms(false);
+ SignatureAndHashAlgorithm sigAlg = TlsUtilities.ChooseSignatureAndHashAlgorithm(ProtocolVersion.TLSv12,
+ supportedSignatureAlgorithms, signatureAlgorithm);
+ Assert.AreEqual(HashAlgorithm.sha256, sigAlg.Hash);
+
+ for (int count = 0; count < 10; ++count)
+ {
+ supportedSignatureAlgorithms = GetSignatureAlgorithms(true);
+ sigAlg = TlsUtilities.ChooseSignatureAndHashAlgorithm(ProtocolVersion.TLSv12,
+ supportedSignatureAlgorithms, signatureAlgorithm);
+ Assert.AreEqual(HashAlgorithm.sha256, sigAlg.Hash);
+ }
+ }
+
+ private static IList GetSignatureAlgorithms(bool randomise)
+ {
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.sha1, HashAlgorithm.sha224, HashAlgorithm.sha256,
+ HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.md5 };
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa, SignatureAlgorithm.dsa,
+ SignatureAlgorithm.ecdsa };
+
+ IList result = new ArrayList();
+ for (int i = 0; i < signatureAlgorithms.Length; ++i)
+ {
+ for (int j = 0; j < hashAlgorithms.Length; ++j)
+ {
+ result.Add(SignatureAndHashAlgorithm.GetInstance(hashAlgorithms[j], signatureAlgorithms[i]));
+ }
+ }
+
+ if (randomise)
+ {
+ Random r = new Random();
+ int count = result.Count;
+ for (int src = 0; src < count; ++src)
+ {
+ int dst = r.Next(count);
+ if (src != dst)
+ {
+ object a = result[src], b = result[dst];
+ result[dst] = a;
+ result[src] = b;
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
new file mode 100644
index 000000000..bdbfd6e67
--- /dev/null
+++ b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
@@ -0,0 +1,79 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class UnreliableDatagramTransport
+ : DatagramTransport
+ {
+ private readonly DatagramTransport m_transport;
+ private readonly Random m_random;
+ private readonly int m_percentPacketLossReceiving, m_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.m_transport = transport;
+ this.m_random = random;
+ this.m_percentPacketLossReceiving = percentPacketLossReceiving;
+ this.m_percentPacketLossSending = percentPacketLossSending;
+ }
+
+ public virtual int GetReceiveLimit()
+ {
+ return m_transport.GetReceiveLimit();
+ }
+
+ public virtual int GetSendLimit()
+ {
+ return m_transport.GetSendLimit();
+ }
+
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis;
+ for (;;)
+ {
+ int length = m_transport.Receive(buf, off, len, waitMillis);
+ if (length < 0 || !LostPacket(m_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(m_percentPacketLossSending))
+ {
+ Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)");
+ }
+ else
+ {
+ m_transport.Send(buf, off, len);
+ }
+ }
+
+ public virtual void Close()
+ {
+ m_transport.Close();
+ }
+
+ private bool LostPacket(int percentPacketLoss)
+ {
+ return percentPacketLoss > 0 && m_random.Next(100) < percentPacketLoss;
+ }
+ }
+}
|