diff options
37 files changed, 957 insertions, 325 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index a8f95a2bf..7e0a21bda 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -4269,6 +4269,36 @@ BuildAction = "Compile" /> <File + RelPath = "src\crypto\tls\AbstractTlsAgreementCredentials.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\crypto\tls\AbstractTlsContext.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\crypto\tls\AbstractTlsCredentials.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\crypto\tls\AbstractTlsEncryptionCredentials.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\crypto\tls\AbstractTlsSigner.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\crypto\tls\AbstractTlsSignerCredentials.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\crypto\tls\AlertDescription.cs" SubType = "Code" BuildAction = "Compile" @@ -4389,6 +4419,11 @@ BuildAction = "Compile" /> <File + RelPath = "src\crypto\tls\DefaultTlsEncryptionCredentials.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\crypto\tls\DefaultTlsSignerCredentials.cs" SubType = "Code" BuildAction = "Compile" @@ -4674,6 +4709,11 @@ BuildAction = "Compile" /> <File + RelPath = "src\crypto\tls\TlsEncryptionCredentials.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\crypto\tls\TlsFatalAlert.cs" SubType = "Code" BuildAction = "Compile" @@ -4739,6 +4779,16 @@ BuildAction = "Compile" /> <File + RelPath = "src\crypto\tls\TlsServerContext.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\crypto\tls\TlsServerContextImpl.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\crypto\tls\TlsSession.cs" SubType = "Code" BuildAction = "Compile" diff --git a/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs new file mode 100644 index 000000000..2d7af80e8 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs @@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsAgreementCredentials + : AbstractTlsCredentials, TlsAgreementCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs new file mode 100644 index 000000000..7a7e636d2 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsContext.cs @@ -0,0 +1,117 @@ +using System; +using System.Threading; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal abstract class AbstractTlsContext + : TlsContext + { + private static long counter = Times.NanoTime(); + + private static long NextCounterValue() + { + return Interlocked.Increment(ref counter); + } + + private readonly IRandomGenerator mNonceRandom; + private readonly SecureRandom mSecureRandom; + private readonly SecurityParameters mSecurityParameters; + + private ProtocolVersion mClientVersion = null; + private ProtocolVersion mServerVersion = null; + private TlsSession mSession = null; + private object mUserObject = null; + + internal AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters) + { + secureRandom.SetSeed(NextCounterValue()); + secureRandom.SetSeed(Times.NanoTime()); + + this.mNonceRandom = new DigestRandomGenerator(TlsUtilities.CreateHash(HashAlgorithm.sha256)); + this.mNonceRandom.AddSeedMaterial(secureRandom.GenerateSeed(32)); + + this.mSecureRandom = secureRandom; + this.mSecurityParameters = securityParameters; + } + + public virtual IRandomGenerator NonceRandomGenerator + { + get { return mNonceRandom; } + } + + public virtual SecureRandom SecureRandom + { + get { return mSecureRandom; } + } + + public virtual SecurityParameters SecurityParameters + { + get { return mSecurityParameters; } + } + + public abstract bool IsServer { get; } + + public virtual ProtocolVersion ClientVersion + { + get { return mClientVersion; } + set { this.mClientVersion = value; } + } + + public virtual ProtocolVersion ServerVersion + { + get { return mServerVersion; } + set { this.mServerVersion = value; } + } + + public virtual TlsSession ResumableSession + { + get { return mSession; } + set { this.mSession = value; } + } + + public virtual object UserObject + { + get { return mUserObject; } + set { this.mUserObject = value; } + } + + public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length) + { + if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length)) + throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value"); + + SecurityParameters sp = SecurityParameters; + byte[] cr = sp.ClientRandom, sr = sp.ServerRandom; + + int seedLength = cr.Length + sr.Length; + if (context_value != null) + { + seedLength += (2 + context_value.Length); + } + + byte[] seed = new byte[seedLength]; + int seedPos = 0; + + Array.Copy(cr, 0, seed, seedPos, cr.Length); + seedPos += cr.Length; + Array.Copy(sr, 0, seed, seedPos, sr.Length); + seedPos += sr.Length; + if (context_value != null) + { + TlsUtilities.WriteUint16(context_value.Length, seed, seedPos); + seedPos += 2; + Array.Copy(context_value, 0, seed, seedPos, context_value.Length); + seedPos += context_value.Length; + } + + if (seedPos != seedLength) + throw new InvalidOperationException("error in calculation of seed for export"); + + return TlsUtilities.PRF(this, sp.MasterSecret, asciiLabel, seed, length); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsCredentials.cs b/crypto/src/crypto/tls/AbstractTlsCredentials.cs new file mode 100644 index 000000000..6411b811c --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsCredentials.cs @@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsCredentials + : TlsCredentials + { + public abstract Certificate Certificate { get; } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs new file mode 100644 index 000000000..05b129c60 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs @@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsEncryptionCredentials + : AbstractTlsCredentials, TlsEncryptionCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsSigner.cs b/crypto/src/crypto/tls/AbstractTlsSigner.cs new file mode 100644 index 000000000..1f4aabf74 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsSigner.cs @@ -0,0 +1,50 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsSigner + : TlsSigner + { + protected TlsContext mContext; + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + { + return GenerateRawSignature(null, privateKey, md5AndSha1); + } + + public abstract byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash); + + public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + { + return VerifyRawSignature(null, sigBytes, publicKey, md5AndSha1); + } + + public abstract bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash); + + public virtual ISigner CreateSigner(AsymmetricKeyParameter privateKey) + { + return CreateSigner(null, privateKey); + } + + public abstract ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); + + public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + { + return CreateVerifyer(null, publicKey); + } + + public abstract ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); + + public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs new file mode 100644 index 000000000..886c46c6e --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs @@ -0,0 +1,20 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsSignerCredentials + : AbstractTlsCredentials, TlsSignerCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] GenerateCertificateSignature(byte[] hash); + + public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get + { + throw new InvalidOperationException("TlsSignerCredentials implementation does not support (D)TLS 1.2+"); + } + } + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs index 2bd2f40bf..5147a1990 100644 --- a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs +++ b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.Parameters; @@ -8,69 +9,61 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { public class DefaultTlsAgreementCredentials - : TlsAgreementCredentials + : AbstractTlsAgreementCredentials { - protected Certificate clientCert; - protected AsymmetricKeyParameter clientPrivateKey; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; - protected IBasicAgreement basicAgreement; - protected bool truncateAgreement; + protected readonly IBasicAgreement mBasicAgreement; + protected readonly bool mTruncateAgreement; - public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey) + public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey) { - if (clientCertificate == null) - { - throw new ArgumentNullException("clientCertificate"); - } - if (clientCertificate.Length == 0) - { - throw new ArgumentException("cannot be empty", "clientCertificate"); - } - if (clientPrivateKey == null) - { - throw new ArgumentNullException("clientPrivateKey"); - } - if (!clientPrivateKey.IsPrivate) - { - throw new ArgumentException("must be private", "clientPrivateKey"); - } + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "certificate"); + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); - if (clientPrivateKey is DHPrivateKeyParameters) + if (privateKey is DHPrivateKeyParameters) { - basicAgreement = new DHBasicAgreement(); - truncateAgreement = true; + mBasicAgreement = new DHBasicAgreement(); + mTruncateAgreement = true; } - else if (clientPrivateKey is ECPrivateKeyParameters) + else if (privateKey is ECPrivateKeyParameters) { - basicAgreement = new ECDHBasicAgreement(); - truncateAgreement = false; + mBasicAgreement = new ECDHBasicAgreement(); + mTruncateAgreement = false; } else { - throw new ArgumentException("type not supported: " - + clientPrivateKey.GetType().FullName, "clientPrivateKey"); + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); } - this.clientCert = clientCertificate; - this.clientPrivateKey = clientPrivateKey; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; } - public virtual Certificate Certificate + public override Certificate Certificate { - get { return clientCert; } + get { return mCertificate; } } - public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey) + /// <exception cref="IOException"></exception> + public override byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey) { - basicAgreement.Init(clientPrivateKey); - BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey); + mBasicAgreement.Init(mPrivateKey); + BigInteger agreementValue = mBasicAgreement.CalculateAgreement(peerPublicKey); - if (truncateAgreement) + if (mTruncateAgreement) { return BigIntegers.AsUnsignedByteArray(agreementValue); } - return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue); + return BigIntegers.AsUnsignedByteArray(mBasicAgreement.GetFieldSize(), agreementValue); } } } diff --git a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs index 18b23a67b..cc34b3028 100644 --- a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs +++ b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs @@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Tls public class DefaultTlsCipherFactory : TlsCipherFactory { - public virtual TlsCipher CreateCipher(TlsClientContext context, + public virtual TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, DigestAlgorithm digestAlgorithm) { switch (encryptionAlgorithm) @@ -29,13 +29,13 @@ namespace Org.BouncyCastle.Crypto.Tls } /// <exception cref="IOException"></exception> - protected virtual TlsCipher CreateRC4Cipher(TlsClientContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm) + protected virtual TlsCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm) { return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize); } /// <exception cref="IOException"></exception> - protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize, + protected virtual TlsCipher CreateAesCipher(TlsContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm) { return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(), @@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Crypto.Tls } /// <exception cref="IOException"></exception> - protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize, + protected virtual TlsCipher CreateDesEdeCipher(TlsContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm) { return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(), diff --git a/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs new file mode 100644 index 000000000..34d15d146 --- /dev/null +++ b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsEncryptionCredentials + : AbstractTlsEncryptionCredentials + { + protected readonly TlsContext mContext; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; + + public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate, + AsymmetricKeyParameter privateKey) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "certificate"); + if (privateKey == null) + throw new ArgumentNullException("'privateKey' cannot be null"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); + + if (privateKey is RsaKeyParameters) + { + } + else + { + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); + } + + this.mContext = context; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + } + + public override Certificate Certificate + { + get { return mCertificate; } + } + + /// <exception cref="IOException"></exception> + public override byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret) + { + return TlsRsaUtilities.SafeDecryptPreMasterSecret(mContext, (RsaKeyParameters)mPrivateKey, encryptedPreMasterSecret); + } + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs index 2c5aa3524..8e609938f 100644 --- a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs +++ b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs @@ -1,76 +1,90 @@ using System; +using System.IO; using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Tls { public class DefaultTlsSignerCredentials - : TlsSignerCredentials + : AbstractTlsSignerCredentials { - protected TlsClientContext context; - protected Certificate clientCert; - protected AsymmetricKeyParameter clientPrivateKey; + protected readonly TlsContext mContext; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; + protected readonly SignatureAndHashAlgorithm mSignatureAndHashAlgorithm; - protected TlsSigner clientSigner; + protected readonly TlsSigner mSigner; - public DefaultTlsSignerCredentials(TlsClientContext context, - Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey) + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey) + : this(context, certificate, privateKey, null) { - if (clientCertificate == null) - { - throw new ArgumentNullException("clientCertificate"); - } - if (clientCertificate.Length == 0) - { + } + + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) throw new ArgumentException("cannot be empty", "clientCertificate"); - } - if (clientPrivateKey == null) - { - throw new ArgumentNullException("clientPrivateKey"); - } - if (!clientPrivateKey.IsPrivate) - { - throw new ArgumentException("must be private", "clientPrivateKey"); - } + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); + if (TlsUtilities.IsTlsV12(context) && signatureAndHashAlgorithm == null) + throw new ArgumentException("cannot be null for (D)TLS 1.2+", "signatureAndHashAlgorithm"); - if (clientPrivateKey is RsaKeyParameters) + if (privateKey is RsaKeyParameters) { - clientSigner = new TlsRsaSigner(); + mSigner = new TlsRsaSigner(); } - else if (clientPrivateKey is DsaPrivateKeyParameters) + else if (privateKey is DsaPrivateKeyParameters) { - clientSigner = new TlsDssSigner(); + mSigner = new TlsDssSigner(); } - else if (clientPrivateKey is ECPrivateKeyParameters) + else if (privateKey is ECPrivateKeyParameters) { - clientSigner = new TlsECDsaSigner(); + mSigner = new TlsECDsaSigner(); } else { - throw new ArgumentException("type not supported: " - + clientPrivateKey.GetType().FullName, "clientPrivateKey"); + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); } - this.context = context; - this.clientCert = clientCertificate; - this.clientPrivateKey = clientPrivateKey; + this.mContext = context; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + this.mSignatureAndHashAlgorithm = signatureAndHashAlgorithm; } - public virtual Certificate Certificate + public override Certificate Certificate { - get { return clientCert; } + get { return mCertificate; } } - public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1) + /// <exception cref="IOException"></exception> + public override byte[] GenerateCertificateSignature(byte[] hash) { try { - return clientSigner.GenerateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1); + if (TlsUtilities.IsTlsV12(mContext)) + { + return mSigner.GenerateRawSignature(mSignatureAndHashAlgorithm, mPrivateKey, hash); + } + else + { + return mSigner.GenerateRawSignature(mPrivateKey, hash); + } } catch (CryptoException) { throw new TlsFatalAlert(AlertDescription.internal_error); } } + + public override SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get { return mSignatureAndHashAlgorithm; } + } } } diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs index 9ed3969eb..3aa318da2 100644 --- a/crypto/src/crypto/tls/SecurityParameters.cs +++ b/crypto/src/crypto/tls/SecurityParameters.cs @@ -1,26 +1,46 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Tls { - public class SecurityParameters - { - internal byte[] clientRandom = null; - internal byte[] serverRandom = null; - internal byte[] masterSecret = null; + public class SecurityParameters + { + internal int prfAlgorithm = -1; + internal byte[] masterSecret = null; + internal byte[] clientRandom = null; + internal byte[] serverRandom = null; + + internal virtual void Clear() + { + if (this.masterSecret != null) + { + Arrays.Fill(this.masterSecret, (byte)0); + this.masterSecret = null; + } + } + + /** + * @return {@link PRFAlgorithm} + */ + public virtual int PrfAlgorithm + { + get { return prfAlgorithm; } + } - public byte[] ClientRandom - { - get { return clientRandom; } - } + public virtual byte[] MasterSecret + { + get { return masterSecret; } + } - public byte[] ServerRandom - { - get { return serverRandom; } - } + public virtual byte[] ClientRandom + { + get { return clientRandom; } + } - public byte[] MasterSecret - { - get { return masterSecret; } - } - } + public virtual byte[] ServerRandom + { + get { return serverRandom; } + } + } } diff --git a/crypto/src/crypto/tls/TlsAgreementCredentials.cs b/crypto/src/crypto/tls/TlsAgreementCredentials.cs index 46ee4f90e..7c64072e8 100644 --- a/crypto/src/crypto/tls/TlsAgreementCredentials.cs +++ b/crypto/src/crypto/tls/TlsAgreementCredentials.cs @@ -3,9 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsAgreementCredentials : TlsCredentials - { - /// <exception cref="IOException"></exception> - byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey); - } + public interface TlsAgreementCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey); + } } diff --git a/crypto/src/crypto/tls/TlsBlockCipher.cs b/crypto/src/crypto/tls/TlsBlockCipher.cs index cfbceb25e..7adab1985 100644 --- a/crypto/src/crypto/tls/TlsBlockCipher.cs +++ b/crypto/src/crypto/tls/TlsBlockCipher.cs @@ -15,7 +15,7 @@ namespace Org.BouncyCastle.Crypto.Tls public class TlsBlockCipher : TlsCipher { - protected TlsClientContext context; + protected TlsContext context; protected byte[] randomData; protected IBlockCipher encryptCipher; @@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Crypto.Tls get { return rMac; } } - public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher, + public TlsBlockCipher(TlsContext context, IBlockCipher encryptCipher, IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize) { this.context = context; @@ -51,7 +51,7 @@ namespace Org.BouncyCastle.Crypto.Tls SecurityParameters securityParameters = context.SecurityParameters; - byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion", + byte[] keyBlock = TlsUtilities.PRF(context, securityParameters.masterSecret, "key expansion", TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom), prfSize); diff --git a/crypto/src/crypto/tls/TlsCipherFactory.cs b/crypto/src/crypto/tls/TlsCipherFactory.cs index bd65f8b4b..e5cf96479 100644 --- a/crypto/src/crypto/tls/TlsCipherFactory.cs +++ b/crypto/src/crypto/tls/TlsCipherFactory.cs @@ -6,7 +6,7 @@ namespace Org.BouncyCastle.Crypto.Tls public interface TlsCipherFactory { /// <exception cref="IOException"></exception> - TlsCipher CreateCipher(TlsClientContext context, int encryptionAlgorithm, + TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, DigestAlgorithm digestAlgorithm); } } diff --git a/crypto/src/crypto/tls/TlsClientContext.cs b/crypto/src/crypto/tls/TlsClientContext.cs index dbb10aa76..b077d0aaf 100644 --- a/crypto/src/crypto/tls/TlsClientContext.cs +++ b/crypto/src/crypto/tls/TlsClientContext.cs @@ -4,12 +4,8 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsClientContext - { - SecureRandom SecureRandom { get; } - - SecurityParameters SecurityParameters { get; } - - object UserObject { get; set; } - } + public interface TlsClientContext + : TlsContext + { + } } diff --git a/crypto/src/crypto/tls/TlsClientContextImpl.cs b/crypto/src/crypto/tls/TlsClientContextImpl.cs index 9d5dee232..674d68937 100644 --- a/crypto/src/crypto/tls/TlsClientContextImpl.cs +++ b/crypto/src/crypto/tls/TlsClientContextImpl.cs @@ -4,34 +4,17 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsClientContextImpl - : TlsClientContext - { - private readonly SecureRandom secureRandom; - private readonly SecurityParameters securityParameters; + internal class TlsClientContextImpl + : AbstractTlsContext, TlsClientContext + { + internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + : base(secureRandom, securityParameters) + { + } - private object userObject = null; - - internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) - { - this.secureRandom = secureRandom; - this.securityParameters = securityParameters; - } - - public virtual SecureRandom SecureRandom - { - get { return secureRandom; } - } - - public virtual SecurityParameters SecurityParameters - { - get { return securityParameters; } - } - - public virtual object UserObject - { - get { return userObject; } - set { this.userObject = value; } - } - } + public override bool IsServer + { + get { return false; } + } + } } diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs index 26d76fd3d..272e38143 100644 --- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs @@ -15,7 +15,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal class TlsDHKeyExchange : TlsKeyExchange { - protected TlsClientContext context; + protected TlsContext context; protected int keyExchange; protected TlsSigner tlsSigner; @@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Tls protected TlsAgreementCredentials agreementCredentials; protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null; - internal TlsDHKeyExchange(TlsClientContext context, int keyExchange) + internal TlsDHKeyExchange(TlsContext context, int keyExchange) { switch (keyExchange) { diff --git a/crypto/src/crypto/tls/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs index ee6d6eb44..a9e9a394c 100644 --- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs @@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal class TlsDheKeyExchange : TlsDHKeyExchange { - internal TlsDheKeyExchange(TlsClientContext context, int keyExchange) + internal TlsDheKeyExchange(TlsContext context, int keyExchange) : base(context, keyExchange) { } diff --git a/crypto/src/crypto/tls/TlsDsaSigner.cs b/crypto/src/crypto/tls/TlsDsaSigner.cs index bba114e90..a5ac55974 100644 --- a/crypto/src/crypto/tls/TlsDsaSigner.cs +++ b/crypto/src/crypto/tls/TlsDsaSigner.cs @@ -7,45 +7,77 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - internal abstract class TlsDsaSigner - : TlsSigner + public abstract class TlsDsaSigner + : AbstractTlsSigner { - public virtual byte[] GenerateRawSignature(SecureRandom random, - AsymmetricKeyParameter privateKey, byte[] md5andsha1) + public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) { - ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random)); - // Note: Only use the SHA1 part of the hash - s.BlockUpdate(md5andsha1, 16, 20); - return s.GenerateSignature(); + ISigner signer = MakeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.BlockUpdate(hash, 16, 20); + } + else + { + signer.BlockUpdate(hash, 0, hash.Length); + } + return signer.GenerateSignature(); } - public bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1) + public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) { - ISigner s = MakeSigner(new NullDigest(), false, publicKey); - // Note: Only use the SHA1 part of the hash - s.BlockUpdate(md5andsha1, 16, 20); - return s.VerifySignature(sigBytes); + ISigner signer = MakeSigner(algorithm, true, false, publicKey); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.BlockUpdate(hash, 16, 20); + } + else + { + signer.BlockUpdate(hash, 0, hash.Length); + } + return signer.VerifySignature(sigBytes); } - public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey) + public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) { - return MakeSigner(new Sha1Digest(), true, new ParametersWithRandom(privateKey, random)); + return MakeSigner(algorithm, false, true, privateKey); } - public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) { - return MakeSigner(new Sha1Digest(), false, publicKey); + return MakeSigner(algorithm, false, false, publicKey); } - public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + protected virtual ICipherParameters MakeInitParameters(bool forSigning, ICipherParameters cp) + { + return cp; + } - protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp) + protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning, + ICipherParameters cp) { - ISigner s = new DsaDigestSigner(CreateDsaImpl(), d); - s.Init(forSigning, cp); + if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext)) + throw new InvalidOperationException(); + + // TODO For TLS 1.2+, lift the SHA-1 restriction here + if (algorithm != null && (algorithm.Hash != HashAlgorithm.sha1 || algorithm.Signature != SignatureAlgorithm)) + throw new InvalidOperationException(); + + byte hashAlgorithm = algorithm == null ? HashAlgorithm.sha1 : algorithm.Hash; + IDigest d = raw ? new NullDigest() : TlsUtilities.CreateHash(hashAlgorithm); + + ISigner s = new DsaDigestSigner(CreateDsaImpl(hashAlgorithm), d); + s.Init(forSigning, MakeInitParameters(forSigning, cp)); return s; } - protected abstract IDsa CreateDsaImpl(); + protected abstract byte SignatureAlgorithm { get; } + + protected abstract IDsa CreateDsaImpl(byte hashAlgorithm); } } diff --git a/crypto/src/crypto/tls/TlsDssSigner.cs b/crypto/src/crypto/tls/TlsDssSigner.cs index c6f1abcec..707ef3853 100644 --- a/crypto/src/crypto/tls/TlsDssSigner.cs +++ b/crypto/src/crypto/tls/TlsDssSigner.cs @@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsDssSigner - : TlsDsaSigner - { - public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) - { - return publicKey is DsaPublicKeyParameters; - } + public class TlsDssSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is DsaPublicKeyParameters; + } - protected override IDsa CreateDsaImpl() - { - return new DsaSigner(); - } - } + protected override IDsa CreateDsaImpl(byte hashAlgorithm) + { + return new DsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm))); + } + + protected override byte SignatureAlgorithm + { + get { return Tls.SignatureAlgorithm.dsa; } + } + } } diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs index 65d07a10c..b02d5a4fd 100644 --- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs @@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal class TlsECDHKeyExchange : TlsKeyExchange { - protected TlsClientContext context; + protected TlsContext context; protected int keyExchange; protected TlsSigner tlsSigner; @@ -27,7 +27,7 @@ namespace Org.BouncyCastle.Crypto.Tls protected TlsAgreementCredentials agreementCredentials; protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null; - internal TlsECDHKeyExchange(TlsClientContext context, int keyExchange) + internal TlsECDHKeyExchange(TlsContext context, int keyExchange) { switch (keyExchange) { diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs index a36bff75b..5f66dbf59 100644 --- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs @@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Tls */ internal class TlsECDheKeyExchange : TlsECDHKeyExchange { - internal TlsECDheKeyExchange(TlsClientContext context, int keyExchange) + internal TlsECDheKeyExchange(TlsContext context, int keyExchange) : base(context, keyExchange) { } diff --git a/crypto/src/crypto/tls/TlsECDsaSigner.cs b/crypto/src/crypto/tls/TlsECDsaSigner.cs index 3c30fdc0c..fa9d0b714 100644 --- a/crypto/src/crypto/tls/TlsECDsaSigner.cs +++ b/crypto/src/crypto/tls/TlsECDsaSigner.cs @@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsECDsaSigner - : TlsDsaSigner - { - public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) - { - return publicKey is ECPublicKeyParameters; - } + public class TlsECDsaSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is ECPublicKeyParameters; + } - protected override IDsa CreateDsaImpl() - { - return new ECDsaSigner(); - } - } + protected override IDsa CreateDsaImpl(byte hashAlgorithm) + { + return new ECDsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm))); + } + + protected override byte SignatureAlgorithm + { + get { return Tls.SignatureAlgorithm.ecdsa; } + } + } } diff --git a/crypto/src/crypto/tls/TlsEncryptionCredentials.cs b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs new file mode 100644 index 000000000..52f007006 --- /dev/null +++ b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs @@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsEncryptionCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret); + } +} diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs index 72bf8c5cf..cf1296b14 100644 --- a/crypto/src/crypto/tls/TlsProtocolHandler.cs +++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs @@ -269,9 +269,8 @@ namespace Org.BouncyCastle.Crypto.Tls /* * Calculate our own checksum. */ - byte[] expectedServerVerifyData = TlsUtilities.PRF( - securityParameters.masterSecret, "server finished", - rs.GetCurrentHash(), 12); + byte[] expectedServerVerifyData = TlsUtilities.PRF(tlsClientContext, + securityParameters.masterSecret, ExporterLabel.server_finished, rs.GetCurrentHash(), 12); /* * Compare both checksums. @@ -388,7 +387,7 @@ namespace Org.BouncyCastle.Crypto.Tls continue; // TODO Add session resumption support - ///* + //* // * 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[.] @@ -546,9 +545,8 @@ namespace Org.BouncyCastle.Crypto.Tls */ byte[] pms = this.keyExchange.GeneratePremasterSecret(); - securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret", - TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), - 48); + securityParameters.masterSecret = TlsUtilities.PRF(tlsClientContext, pms, ExporterLabel.master_secret, + TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), 48); // TODO Is there a way to ensure the data is really overwritten? /* @@ -565,8 +563,8 @@ namespace Org.BouncyCastle.Crypto.Tls /* * Send our finished message. */ - byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret, - "client finished", rs.GetCurrentHash(), 12); + byte[] clientVerifyData = TlsUtilities.PRF(tlsClientContext, securityParameters.masterSecret, + ExporterLabel.client_finished, rs.GetCurrentHash(), 12); MemoryStream bos = new MemoryStream(); TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos); @@ -818,19 +816,22 @@ namespace Org.BouncyCastle.Crypto.Tls if (this.tlsClient != null) throw new InvalidOperationException("Connect can only be called once"); - /* - * Send Client hello - * - * First, generate some random data. - */ + this.tlsClient = tlsClient; + this.securityParameters = new SecurityParameters(); - this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), random, - ExporterLabel.client_random); this.tlsClientContext = new TlsClientContextImpl(random, securityParameters); - this.tlsClient = tlsClient; + + this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), + tlsClientContext.NonceRandomGenerator); + this.tlsClient.Init(tlsClientContext); + /* + * Send Client hello + * + * First, send the client_random data. + */ MemoryStream outStr = new MemoryStream(); TlsUtilities.WriteVersion(outStr); outStr.Write(securityParameters.clientRandom, 0, 32); @@ -1160,20 +1161,10 @@ namespace Org.BouncyCastle.Crypto.Tls } } - protected static byte[] CreateRandomBlock(bool useGMTUnixTime, SecureRandom random, string asciiLabel) + protected static byte[] CreateRandomBlock(bool useGMTUnixTime, IRandomGenerator randomGenerator) { - /* - * We use the TLS 1.0 PRF on the SecureRandom output, to guard against RNGs where the raw - * output could be used to recover the internal state. - */ - byte[] secret = new byte[32]; - random.NextBytes(secret); - - byte[] seed = new byte[8]; - // TODO Use high-resolution timer - TlsUtilities.WriteUint64(DateTimeUtilities.CurrentUnixMs(), seed, 0); - - byte[] result = TlsUtilities.PRF(secret, asciiLabel, seed, 32); + byte[] result = new byte[32]; + randomGenerator.NextBytes(result); if (useGMTUnixTime) { diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs index 9961fc9d1..4a5cb4ead 100644 --- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs @@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal class TlsPskKeyExchange : TlsKeyExchange { - protected TlsClientContext context; + protected TlsContext context; protected int keyExchange; protected TlsPskIdentity pskIdentity; @@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Tls protected RsaKeyParameters rsaServerPublicKey = null; protected byte[] premasterSecret; - internal TlsPskKeyExchange(TlsClientContext context, int keyExchange, + internal TlsPskKeyExchange(TlsContext context, int keyExchange, TlsPskIdentity pskIdentity) { switch (keyExchange) @@ -139,7 +139,7 @@ namespace Org.BouncyCastle.Crypto.Tls public virtual void GenerateClientKeyExchange(Stream output) { - if (psk_identity_hint == null || psk_identity_hint.Length == 0) + if (psk_identity_hint == null) { pskIdentity.SkipIdentityHint(); } @@ -152,16 +152,21 @@ namespace Org.BouncyCastle.Crypto.Tls TlsUtilities.WriteOpaque16(psk_identity, output); - if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) - { - this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( - context.SecureRandom, this.rsaServerPublicKey, output); - } - else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) { this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange( context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output); } + else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + throw new TlsFatalAlert(AlertDescription.internal_error); + } + else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( + context, this.rsaServerPublicKey, output); + } } public virtual byte[] GeneratePremasterSecret() diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs index aad482316..0a9fbc67d 100644 --- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs @@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal class TlsRsaKeyExchange : TlsKeyExchange { - protected TlsClientContext context; + protected TlsContext context; protected AsymmetricKeyParameter serverPublicKey = null; @@ -25,7 +25,7 @@ namespace Org.BouncyCastle.Crypto.Tls protected byte[] premasterSecret; - internal TlsRsaKeyExchange(TlsClientContext context) + internal TlsRsaKeyExchange(TlsContext context) { this.context = context; } @@ -110,8 +110,7 @@ namespace Org.BouncyCastle.Crypto.Tls public virtual void GenerateClientKeyExchange(Stream output) { - this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( - context.SecureRandom, this.rsaServerPublicKey, output); + this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output); } public virtual byte[] GeneratePremasterSecret() diff --git a/crypto/src/crypto/tls/TlsRsaSigner.cs b/crypto/src/crypto/tls/TlsRsaSigner.cs index ce18ef5e1..6da1c5e9b 100644 --- a/crypto/src/crypto/tls/TlsRsaSigner.cs +++ b/crypto/src/crypto/tls/TlsRsaSigner.cs @@ -10,50 +10,92 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsRsaSigner - : TlsSigner + public class TlsRsaSigner + : AbstractTlsSigner { - public virtual byte[] GenerateRawSignature(SecureRandom random, - AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) { - IAsymmetricBlockCipher engine = CreateRsaImpl(); - engine.Init(true, new ParametersWithRandom(privateKey, random)); - return engine.ProcessBlock(md5AndSha1, 0, md5AndSha1.Length); + ISigner signer = MakeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + signer.BlockUpdate(hash, 0, hash.Length); + return signer.GenerateSignature(); } - public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, - byte[] md5AndSha1) + public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) { - IAsymmetricBlockCipher engine = CreateRsaImpl(); - engine.Init(false, publicKey); - byte[] signed = engine.ProcessBlock(sigBytes, 0, sigBytes.Length); - return Arrays.ConstantTimeAreEqual(signed, md5AndSha1); + ISigner signer = MakeSigner(algorithm, true, false, publicKey); + signer.BlockUpdate(hash, 0, hash.Length); + return signer.VerifySignature(sigBytes); } - public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey) + public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) { - return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random)); + return MakeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); } - public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) { - return MakeSigner(new CombinedHash(), false, publicKey); + return MakeSigner(algorithm, false, false, publicKey); } - public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) { return publicKey is RsaKeyParameters && !publicKey.IsPrivate; } - protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp) + protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning, + ICipherParameters cp) { - ISigner s = new GenericSigner(CreateRsaImpl(), d); + if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext)) + throw new InvalidOperationException(); + if (algorithm != null && algorithm.Signature != SignatureAlgorithm.rsa) + throw new InvalidOperationException(); + + IDigest d; + if (raw) + { + d = new NullDigest(); + } + else if (algorithm == null) + { + d = new CombinedHash(); + } + else + { + d = TlsUtilities.CreateHash(algorithm.Hash); + } + + ISigner s; + if (algorithm != null) + { + /* + * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated + * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1]. + */ + s = new RsaDigestSigner(d, TlsUtilities.GetOidForHashAlgorithm(algorithm.Hash)); + } + else + { + /* + * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme + * that did not include a DigestInfo encoding. + */ + s = new GenericSigner(CreateRsaImpl(), d); + } s.Init(forSigning, cp); return s; } protected virtual IAsymmetricBlockCipher CreateRsaImpl() { + /* + * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks + * on TLS are possible, at least when the client and server are on the same LAN. + * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other + * anti-timing technique, as described in [TIMING]. + */ return new Pkcs1Encoding(new RsaBlindedEngine()); } } diff --git a/crypto/src/crypto/tls/TlsRsaUtilities.cs b/crypto/src/crypto/tls/TlsRsaUtilities.cs index 4450ba452..de56ffc4d 100644 --- a/crypto/src/crypto/tls/TlsRsaUtilities.cs +++ b/crypto/src/crypto/tls/TlsRsaUtilities.cs @@ -5,38 +5,128 @@ using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public abstract class TlsRsaUtilities - { - public static byte[] GenerateEncryptedPreMasterSecret(SecureRandom random, - RsaKeyParameters rsaServerPublicKey, Stream output) - { - /* - * Choose a PremasterSecret and send it encrypted to the server - */ - byte[] premasterSecret = new byte[48]; - random.NextBytes(premasterSecret); - TlsUtilities.WriteVersion(premasterSecret, 0); - - Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine()); - encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, random)); - - try - { - byte[] keData = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length); - TlsUtilities.WriteOpaque16(keData, output); - } - catch (InvalidCipherTextException) - { - /* - * This should never happen, only during decryption. - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - - return premasterSecret; - } - } + public abstract class TlsRsaUtilities + { + /// <exception cref="IOException"></exception> + public static byte[] GenerateEncryptedPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPublicKey, + Stream output) + { + /* + * Choose a PremasterSecret and send it encrypted to the server + */ + byte[] premasterSecret = new byte[48]; + context.SecureRandom.NextBytes(premasterSecret); + TlsUtilities.WriteVersion(context.ClientVersion, premasterSecret, 0); + + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine()); + encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, context.SecureRandom)); + + try + { + byte[] encryptedPreMasterSecret = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length); + + if (TlsUtilities.IsSsl(context)) + { + // TODO Do any SSLv3 servers actually expect the length? + output.Write(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); + } + else + { + TlsUtilities.WriteOpaque16(encryptedPreMasterSecret, output); + } + } + catch (InvalidCipherTextException) + { + /* + * This should never happen, only during decryption. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return premasterSecret; + } + + public static byte[] SafeDecryptPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPrivateKey, + byte[] encryptedPreMasterSecret) + { + /* + * RFC 5246 7.4.7.1. + */ + ProtocolVersion clientVersion = context.ClientVersion; + + // TODO Provide as configuration option? + bool versionNumberCheckDisabled = false; + + /* + * Generate 48 random bytes we can use as a Pre-Master-Secret, if the + * PKCS1 padding check should fail. + */ + byte[] fallback = new byte[48]; + context.SecureRandom.NextBytes(fallback); + + byte[] M = Arrays.Clone(fallback); + try + { + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback); + encoding.Init(false, + new ParametersWithRandom(rsaServerPrivateKey, context.SecureRandom)); + + M = encoding.ProcessBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); + } + catch (Exception) + { + /* + * This should never happen since the decryption should never throw an exception + * and return a random value instead. + * + * In any case, a TLS server MUST NOT generate an alert if processing an + * RSA-encrypted premaster secret message fails, or the version number is not as + * expected. Instead, it MUST continue the handshake with a randomly generated + * premaster secret. + */ + } + + /* + * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST + * check the version number [..]. + */ + if (versionNumberCheckDisabled && clientVersion.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv10)) + { + /* + * If the version number is TLS 1.0 or earlier, server + * implementations SHOULD check the version number, but MAY have a + * configuration option to disable the check. + * + * So there is nothing to do here. + */ + } + else + { + /* + * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the + * clientVersion received during the handshake. If they don't match, we replace the + * decrypted Pre-Master-Secret with a random one. + */ + int correct = (clientVersion.MajorVersion ^ (M[0] & 0xff)) + | (clientVersion.MinorVersion ^ (M[1] & 0xff)); + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + int mask = ~((correct & 1) - 1); + + /* + * mask will be all bits set to 0xff if the version number differed. + */ + for (int i = 0; i < 48; i++) + { + M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask)); + } + } + return M; + } + } } diff --git a/crypto/src/crypto/tls/TlsServerContext.cs b/crypto/src/crypto/tls/TlsServerContext.cs new file mode 100644 index 000000000..4021571aa --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerContext.cs @@ -0,0 +1,11 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsServerContext + : TlsContext + { + } +} diff --git a/crypto/src/crypto/tls/TlsServerContextImpl.cs b/crypto/src/crypto/tls/TlsServerContextImpl.cs new file mode 100644 index 000000000..d56566ffc --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerContextImpl.cs @@ -0,0 +1,20 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsServerContextImpl + : AbstractTlsContext, TlsServerContext + { + internal TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + : base(secureRandom, securityParameters) + { + } + + public override bool IsServer + { + get { return true; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsSigner.cs b/crypto/src/crypto/tls/TlsSigner.cs index 79d468fee..ffdd4c9a1 100644 --- a/crypto/src/crypto/tls/TlsSigner.cs +++ b/crypto/src/crypto/tls/TlsSigner.cs @@ -1,18 +1,29 @@ using System; -using Org.BouncyCastle.Security; - namespace Org.BouncyCastle.Crypto.Tls { public interface TlsSigner { - byte[] GenerateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey, - byte[] md5andsha1); - bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1); + void Init(TlsContext context); + + byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1); + + byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash); + + bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1); + + bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash); + + ISigner CreateSigner(AsymmetricKeyParameter privateKey); + + ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); - ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey); ISigner CreateVerifyer(AsymmetricKeyParameter publicKey); + ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); + bool IsValidPublicKey(AsymmetricKeyParameter publicKey); } } diff --git a/crypto/src/crypto/tls/TlsSignerCredentials.cs b/crypto/src/crypto/tls/TlsSignerCredentials.cs index 2adb06c26..92ed7cc19 100644 --- a/crypto/src/crypto/tls/TlsSignerCredentials.cs +++ b/crypto/src/crypto/tls/TlsSignerCredentials.cs @@ -3,9 +3,12 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsSignerCredentials : TlsCredentials - { - /// <exception cref="IOException"></exception> - byte[] GenerateCertificateSignature(byte[] md5andsha1); - } + public interface TlsSignerCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateCertificateSignature(byte[] hash); + + SignatureAndHashAlgorithm SignatureAndHashAlgorithm { get; } + } } diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs index 950be87ba..46e0e02b2 100644 --- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs @@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal class TlsSrpKeyExchange : TlsKeyExchange { - protected TlsClientContext context; + protected TlsContext context; protected int keyExchange; protected TlsSigner tlsSigner; protected byte[] identity; @@ -33,7 +33,7 @@ namespace Org.BouncyCastle.Crypto.Tls protected BigInteger B = null; protected Srp6Client srpClient = new Srp6Client(); - internal TlsSrpKeyExchange(TlsClientContext context, int keyExchange, + internal TlsSrpKeyExchange(TlsContext context, int keyExchange, byte[] identity, byte[] password) { switch (keyExchange) diff --git a/crypto/src/crypto/tls/TlsStreamCipher.cs b/crypto/src/crypto/tls/TlsStreamCipher.cs index 35f794d96..3e6f7e06d 100644 --- a/crypto/src/crypto/tls/TlsStreamCipher.cs +++ b/crypto/src/crypto/tls/TlsStreamCipher.cs @@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Tls { public class TlsStreamCipher : TlsCipher { - protected TlsClientContext context; + protected TlsContext context; protected IStreamCipher encryptCipher; protected IStreamCipher decryptCipher; @@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Tls protected TlsMac writeMac; protected TlsMac readMac; - public TlsStreamCipher(TlsClientContext context, IStreamCipher encryptCipher, + public TlsStreamCipher(TlsContext context, IStreamCipher encryptCipher, IStreamCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize) { this.context = context; @@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Crypto.Tls SecurityParameters securityParameters = context.SecurityParameters; - byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion", + byte[] keyBlock = TlsUtilities.PRF(context, securityParameters.masterSecret, "key expansion", TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom), prfSize); diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index ffb2fc3e6..462ec4074 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -4,6 +4,8 @@ using System.IO; using System.Text; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Macs; @@ -660,37 +662,54 @@ namespace Org.BouncyCastle.Crypto.Tls } } - internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size) + public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size) { - byte[] label = Strings.ToAsciiByteArray(asciiLabel); + ProtocolVersion version = context.ServerVersion; - int s_half = (secret.Length + 1) / 2; - byte[] s1 = new byte[s_half]; - byte[] s2 = new byte[s_half]; - Array.Copy(secret, 0, s1, 0, s_half); - Array.Copy(secret, secret.Length - s_half, s2, 0, s_half); + if (version.IsSsl) + throw new InvalidOperationException("No PRF available for SSLv3 session"); + + byte[] label = Strings.ToByteArray(asciiLabel); + byte[] labelSeed = Concat(label, seed); - byte[] ls = Concat(label, seed); + int prfAlgorithm = context.SecurityParameters.PrfAlgorithm; - byte[] buf = new byte[size]; - byte[] prf = new byte[size]; - HMacHash(new MD5Digest(), s1, ls, prf); - HMacHash(new Sha1Digest(), s2, ls, buf); - for (int i = 0; i < size; i++) + if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy) { - buf[i] ^= prf[i]; + return PRF_legacy(secret, label, labelSeed, size); } + + IDigest prfDigest = CreatePrfHash(prfAlgorithm); + byte[] buf = new byte[size]; + HMacHash(prfDigest, secret, labelSeed, buf); return buf; } - internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size) + public static byte[] PRF_legacy(byte[] secret, string asciiLabel, byte[] seed, int size) { - byte[] label = Strings.ToAsciiByteArray(asciiLabel); + byte[] label = Strings.ToByteArray(asciiLabel); byte[] labelSeed = Concat(label, seed); - byte[] buf = new byte[size]; - HMacHash(digest, secret, labelSeed, buf); - return buf; + return PRF_legacy(secret, label, labelSeed, size); + } + + internal static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size) + { + int s_half = (secret.Length + 1) / 2; + byte[] s1 = new byte[s_half]; + byte[] s2 = new byte[s_half]; + Array.Copy(secret, 0, s1, 0, s_half); + Array.Copy(secret, secret.Length - s_half, s2, 0, s_half); + + byte[] b1 = new byte[size]; + byte[] b2 = new byte[size]; + HMacHash(CreateHash(HashAlgorithm.md5), s1, labelSeed, b1); + HMacHash(CreateHash(HashAlgorithm.sha1), s2, labelSeed, b2); + for (int i = 0; i < size; i++) + { + b1[i] ^= b2[i]; + } + return b1; } internal static byte[] Concat(byte[] a, byte[] b) @@ -782,6 +801,64 @@ namespace Org.BouncyCastle.Crypto.Tls } } + public static IDigest CreatePrfHash(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + return new CombinedHash(); + default: + return CreateHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm)); + } + } + + public static IDigest ClonePrfHash(int prfAlgorithm, IDigest hash) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + return new CombinedHash((CombinedHash)hash); + default: + return CloneHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm), hash); + } + } + + public static byte GetHashAlgorithmForPrfAlgorithm(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + throw new ArgumentException("legacy PRF not a valid algorithm", "prfAlgorithm"); + case PrfAlgorithm.tls_prf_sha256: + return HashAlgorithm.sha256; + case PrfAlgorithm.tls_prf_sha384: + return HashAlgorithm.sha384; + default: + throw new ArgumentException("unknown PrfAlgorithm", "prfAlgorithm"); + } + } + + public static DerObjectIdentifier GetOidForHashAlgorithm(byte hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return PkcsObjectIdentifiers.MD5; + case HashAlgorithm.sha1: + return X509ObjectIdentifiers.IdSha1; + case HashAlgorithm.sha224: + return NistObjectIdentifiers.IdSha224; + case HashAlgorithm.sha256: + return NistObjectIdentifiers.IdSha256; + case HashAlgorithm.sha384: + return NistObjectIdentifiers.IdSha384; + case HashAlgorithm.sha512: + return NistObjectIdentifiers.IdSha512; + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + private static IList VectorOfOne(object obj) { IList v = Platform.CreateArrayList(1); |