From 106e5917d25734026ee7ea268bc264544f3e02f7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 22 Jan 2015 16:41:29 +0700 Subject: Port of recent TLS-PSK work from Java build --- crypto/crypto.csproj | 15 ++ crypto/src/crypto/tls/BasicTlsPskIdentity.cs | 43 +++ crypto/src/crypto/tls/DtlsClientProtocol.cs | 1 + crypto/src/crypto/tls/PskTlsClient.cs | 17 +- crypto/src/crypto/tls/PskTlsServer.cs | 347 +++++++++++++++++++++++++ crypto/src/crypto/tls/SecurityParameters.cs | 6 + crypto/src/crypto/tls/ServerDHParams.cs | 3 +- crypto/src/crypto/tls/SessionParameters.cs | 20 +- crypto/src/crypto/tls/TlsECDheKeyExchange.cs | 67 +---- crypto/src/crypto/tls/TlsEccUtilities.cs | 63 +++++ crypto/src/crypto/tls/TlsPskIdentityManager.cs | 11 + crypto/src/crypto/tls/TlsPskKeyExchange.cs | 97 ++++++- 12 files changed, 605 insertions(+), 85 deletions(-) create mode 100644 crypto/src/crypto/tls/BasicTlsPskIdentity.cs create mode 100644 crypto/src/crypto/tls/PskTlsServer.cs create mode 100644 crypto/src/crypto/tls/TlsPskIdentityManager.cs (limited to 'crypto') diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 74aac8b6e..fdd5c152b 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -4343,6 +4343,11 @@ SubType = "Code" BuildAction = "Compile" /> + + + = 0, "compressionAlgorithm"); Validate(this.mMasterSecret != null, "masterSecret"); return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate, - mEncodedServerExtensions); + mPskIdentity, mEncodedServerExtensions); } public Builder SetCipherSuite(int cipherSuite) @@ -53,6 +54,12 @@ namespace Org.BouncyCastle.Crypto.Tls return this; } + public Builder SetPskIdentity(byte[] pskIdentity) + { + this.mPskIdentity = pskIdentity; + return this; + } + public Builder SetServerExtensions(IDictionary serverExtensions) { if (serverExtensions == null) @@ -79,15 +86,17 @@ namespace Org.BouncyCastle.Crypto.Tls private byte mCompressionAlgorithm; private byte[] mMasterSecret; private Certificate mPeerCertificate; + private byte[] mPskIdentity; private byte[] mEncodedServerExtensions; private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret, - Certificate peerCertificate, byte[] encodedServerExtensions) + Certificate peerCertificate, byte[] pskIdentity, byte[] encodedServerExtensions) { this.mCipherSuite = cipherSuite; this.mCompressionAlgorithm = compressionAlgorithm; this.mMasterSecret = Arrays.Clone(masterSecret); this.mPeerCertificate = peerCertificate; + this.mPskIdentity = Arrays.Clone(pskIdentity); this.mEncodedServerExtensions = encodedServerExtensions; } @@ -102,7 +111,7 @@ namespace Org.BouncyCastle.Crypto.Tls public SessionParameters Copy() { return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate, - mEncodedServerExtensions); + mPskIdentity, mEncodedServerExtensions); } public int CipherSuite @@ -125,6 +134,11 @@ namespace Org.BouncyCastle.Crypto.Tls get { return mPeerCertificate; } } + public byte[] PskIdentity + { + get { return mPskIdentity; } + } + public IDictionary ReadServerExtensions() { if (mEncodedServerExtensions == null) diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs index 0644bd44d..b99db0c18 100644 --- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs @@ -34,73 +34,10 @@ namespace Org.BouncyCastle.Crypto.Tls public override byte[] GenerateServerKeyExchange() { - /* - * First we try to find a supported named curve from the client's list. - */ - int namedCurve = -1; - if (mNamedCurves == null) - { - // TODO Let the peer choose the default named curve - namedCurve = NamedCurve.secp256r1; - } - else - { - for (int i = 0; i < mNamedCurves.Length; ++i) - { - int entry = mNamedCurves[i]; - if (NamedCurve.IsValid(entry) && TlsEccUtilities.IsSupportedNamedCurve(entry)) - { - namedCurve = entry; - break; - } - } - } - - ECDomainParameters curve_params = null; - if (namedCurve >= 0) - { - curve_params = TlsEccUtilities.GetParametersForNamedCurve(namedCurve); - } - else - { - /* - * If no named curves are suitable, check if the client supports explicit curves. - */ - if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_prime_curves)) - { - curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.secp256r1); - } - else if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_char2_curves)) - { - curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.sect283r1); - } - } - - if (curve_params == null) - { - /* - * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find - * a suitable curve. - */ - throw new TlsFatalAlert(AlertDescription.internal_error); - } - - AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(context.SecureRandom, curve_params); - this.mECAgreePrivateKey = (ECPrivateKeyParameters)kp.Private; - DigestInputBuffer buf = new DigestInputBuffer(); - if (namedCurve < 0) - { - TlsEccUtilities.WriteExplicitECParameters(mClientECPointFormats, curve_params, buf); - } - else - { - TlsEccUtilities.WriteNamedECParameters(namedCurve, buf); - } - - ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public; - TlsEccUtilities.WriteECPoint(mClientECPointFormats, ecPublicKey.Q, buf); + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom, mNamedCurves, + mClientECPointFormats, buf); /* * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs index 34f0f57ba..e938b1685 100644 --- a/crypto/src/crypto/tls/TlsEccUtilities.cs +++ b/crypto/src/crypto/tls/TlsEccUtilities.cs @@ -435,6 +435,69 @@ namespace Org.BouncyCastle.Crypto.Tls return (ECPrivateKeyParameters)kp.Private; } + // TODO Refactor around ServerECDHParams before making this public + internal static ECPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves, + byte[] ecPointFormats, Stream output) + { + /* First we try to find a supported named curve from the client's list. */ + int namedCurve = -1; + if (namedCurves == null) + { + // TODO Let the peer choose the default named curve + namedCurve = NamedCurve.secp256r1; + } + else + { + for (int i = 0; i < namedCurves.Length; ++i) + { + int entry = namedCurves[i]; + if (NamedCurve.IsValid(entry) && IsSupportedNamedCurve(entry)) + { + namedCurve = entry; + break; + } + } + } + + ECDomainParameters ecParams = null; + if (namedCurve >= 0) + { + ecParams = GetParametersForNamedCurve(namedCurve); + } + else + { + /* If no named curves are suitable, check if the client supports explicit curves. */ + if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves)) + { + ecParams = GetParametersForNamedCurve(NamedCurve.secp256r1); + } + else if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves)) + { + ecParams = GetParametersForNamedCurve(NamedCurve.sect283r1); + } + } + + if (ecParams == null) + { + /* + * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find + * a suitable curve. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (namedCurve < 0) + { + WriteExplicitECParameters(ecPointFormats, ecParams, output); + } + else + { + WriteNamedECParameters(namedCurve, output); + } + + return GenerateEphemeralClientKeyExchange(random, ecPointFormats, ecParams, output); + } + public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) { // TODO Check RFC 4492 for validation diff --git a/crypto/src/crypto/tls/TlsPskIdentityManager.cs b/crypto/src/crypto/tls/TlsPskIdentityManager.cs new file mode 100644 index 000000000..a72c2299c --- /dev/null +++ b/crypto/src/crypto/tls/TlsPskIdentityManager.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsPskIdentityManager + { + byte[] GetHint(); + + byte[] GetPsk(byte[] identity); + } +} diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs index cd13e3438..a8d0867ef 100644 --- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs @@ -4,7 +4,10 @@ using System.IO; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { @@ -13,22 +16,29 @@ namespace Org.BouncyCastle.Crypto.Tls : AbstractTlsKeyExchange { protected TlsPskIdentity mPskIdentity; + protected TlsPskIdentityManager mPskIdentityManager; + protected DHParameters mDHParameters; protected int[] mNamedCurves; protected byte[] mClientECPointFormats, mServerECPointFormats; protected byte[] mPskIdentityHint = null; + protected byte[] mPsk = null; protected DHPrivateKeyParameters mDHAgreePrivateKey = null; protected DHPublicKeyParameters mDHAgreePublicKey = null; + protected ECPrivateKeyParameters mECAgreePrivateKey = null; + protected ECPublicKeyParameters mECAgreePublicKey = null; + protected AsymmetricKeyParameter mServerPublicKey = null; protected RsaKeyParameters mRsaServerPublicKey = null; protected TlsEncryptionCredentials mServerCredentials = null; protected byte[] mPremasterSecret; public TlsPskKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsPskIdentity pskIdentity, - DHParameters dhParameters, int[] namedCurves, byte[] clientECPointFormats, byte[] serverECPointFormats) + TlsPskIdentityManager pskIdentityManager, DHParameters dhParameters, int[] namedCurves, + byte[] clientECPointFormats, byte[] serverECPointFormats) : base(keyExchange, supportedSignatureAlgorithms) { switch (keyExchange) @@ -43,6 +53,7 @@ namespace Org.BouncyCastle.Crypto.Tls } this.mPskIdentity = pskIdentity; + this.mPskIdentityManager = pskIdentityManager; this.mDHParameters = dhParameters; this.mNamedCurves = namedCurves; this.mClientECPointFormats = clientECPointFormats; @@ -67,8 +78,7 @@ namespace Org.BouncyCastle.Crypto.Tls public override byte[] GenerateServerKeyExchange() { - // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys - this.mPskIdentityHint = null; + this.mPskIdentityHint = mPskIdentityManager.GetHint(); if (this.mPskIdentityHint == null && !RequiresServerKeyExchange) return null; @@ -94,7 +104,8 @@ namespace Org.BouncyCastle.Crypto.Tls } else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - // TODO[RFC 5489] + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom, + mNamedCurves, mClientECPointFormats, buf); } return buf.ToArray(); @@ -157,7 +168,12 @@ namespace Org.BouncyCastle.Crypto.Tls } else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - // TODO[RFC 5489] + ECDomainParameters ecParams = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, input); + + byte[] point = TlsUtilities.ReadOpaque8(input); + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mClientECPointFormats, ecParams, point)); } } @@ -183,9 +199,17 @@ namespace Org.BouncyCastle.Crypto.Tls } byte[] psk_identity = mPskIdentity.GetPskIdentity(); + if (psk_identity == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.mPsk = mPskIdentity.GetPsk(); + if (mPsk == null) + throw new TlsFatalAlert(AlertDescription.internal_error); TlsUtilities.WriteOpaque16(psk_identity, output); + context.SecurityParameters.pskIdentity = psk_identity; + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) { this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom, @@ -193,8 +217,8 @@ namespace Org.BouncyCastle.Crypto.Tls } else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - // TODO[RFC 5489] - throw new TlsFatalAlert(AlertDescription.internal_error); + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom, + mServerECPointFormats, mECAgreePublicKey.Parameters, output); } else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) { @@ -203,14 +227,59 @@ namespace Org.BouncyCastle.Crypto.Tls } } + public override void ProcessClientKeyExchange(Stream input) + { + byte[] psk_identity = TlsUtilities.ReadOpaque16(input); + + this.mPsk = mPskIdentityManager.GetPsk(psk_identity); + if (mPsk == null) + throw new TlsFatalAlert(AlertDescription.unknown_psk_identity); + + context.SecurityParameters.pskIdentity = psk_identity; + + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + BigInteger Yc = TlsDHUtilities.ReadDHParameter(input); + + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Yc, mDHParameters)); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + byte[] point = TlsUtilities.ReadOpaque8(input); + + ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters; + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mServerECPointFormats, curve_params, point)); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + byte[] encryptedPreMasterSecret; + if (TlsUtilities.IsSsl(context)) + { + // TODO Do any SSLv3 clients actually include the length? + encryptedPreMasterSecret = Streams.ReadAll(input); + } + else + { + encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input); + } + + this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret); + } + } + public override byte[] GeneratePremasterSecret() { - byte[] psk = mPskIdentity.GetPsk(); - byte[] other_secret = GenerateOtherSecret(psk.Length); + byte[] other_secret = GenerateOtherSecret(mPsk.Length); - MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length); + MemoryStream buf = new MemoryStream(4 + other_secret.Length + mPsk.Length); TlsUtilities.WriteOpaque16(other_secret, buf); - TlsUtilities.WriteOpaque16(psk, buf); + TlsUtilities.WriteOpaque16(mPsk, buf); + + Arrays.Fill(mPsk, (byte)0); + this.mPsk = null; + return buf.ToArray(); } @@ -228,7 +297,11 @@ namespace Org.BouncyCastle.Crypto.Tls if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) { - // TODO[RFC 5489] + if (mECAgreePrivateKey != null) + { + return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey); + } + throw new TlsFatalAlert(AlertDescription.internal_error); } -- cgit 1.5.1