diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-08-22 18:22:13 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-08-22 18:22:13 +0700 |
commit | 91443f862f00e62fe69a28e4afee7c00c1c264bd (patch) | |
tree | bd81f549a40d72ae82ecaddf6714c9285ca0de87 /crypto/src | |
parent | More TLS porting from Java API (diff) | |
download | BouncyCastle.NET-ed25519-91443f862f00e62fe69a28e4afee7c00c1c264bd.tar.xz |
More TLS porting from Java API
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/tls/CombinedHash.cs | 203 | ||||
-rw-r--r-- | crypto/src/crypto/tls/DeferredHash.cs | 199 | ||||
-rw-r--r-- | crypto/src/crypto/tls/DigitallySigned.cs | 70 | ||||
-rw-r--r-- | crypto/src/crypto/tls/LegacyTlsAuthentication.cs | 9 | ||||
-rw-r--r-- | crypto/src/crypto/tls/ServerDHParams.cs | 60 | ||||
-rw-r--r-- | crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs | 15 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsDHUtilities.cs | 46 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsEccUtilities.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsExtensionsUtilities.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsSrpUtilities.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsSrtpUtilities.cs | 62 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsStream.cs | 120 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsUtilities.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/UseSrtpData.cs | 56 |
14 files changed, 691 insertions, 157 deletions
diff --git a/crypto/src/crypto/tls/CombinedHash.cs b/crypto/src/crypto/tls/CombinedHash.cs index 59ad87a7b..74a52d598 100644 --- a/crypto/src/crypto/tls/CombinedHash.cs +++ b/crypto/src/crypto/tls/CombinedHash.cs @@ -1,82 +1,133 @@ using System; -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - /// <remarks>A combined hash, which implements md5(m) || sha1(m).</remarks> - internal class CombinedHash - : IDigest - { - private readonly MD5Digest md5; - private readonly Sha1Digest sha1; - - internal CombinedHash() - { - this.md5 = new MD5Digest(); - this.sha1 = new Sha1Digest(); - } - - internal CombinedHash(CombinedHash t) - { - this.md5 = new MD5Digest(t.md5); - this.sha1 = new Sha1Digest(t.sha1); - } - - /// <seealso cref="IDigest.AlgorithmName"/> - public string AlgorithmName - { - get - { - return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0"; - } - } - - /// <seealso cref="IDigest.GetByteLength"/> - public int GetByteLength() - { - return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength()); - } - - /// <seealso cref="IDigest.GetDigestSize"/> - public int GetDigestSize() - { - return md5.GetDigestSize() + sha1.GetDigestSize(); - } - - /// <seealso cref="IDigest.Update"/> - public void Update( - byte input) - { - md5.Update(input); - sha1.Update(input); - } - - /// <seealso cref="IDigest.BlockUpdate"/> - public void BlockUpdate( - byte[] input, - int inOff, - int len) - { - md5.BlockUpdate(input, inOff, len); - sha1.BlockUpdate(input, inOff, len); - } - - /// <seealso cref="IDigest.DoFinal"/> - public int DoFinal( - byte[] output, - int outOff) - { - int i1 = md5.DoFinal(output, outOff); - int i2 = sha1.DoFinal(output, outOff + i1); - return i1 + i2; - } - - /// <seealso cref="IDigest.Reset"/> - public void Reset() - { - md5.Reset(); - sha1.Reset(); - } - } + /** + * A combined hash, which implements md5(m) || sha1(m). + */ + internal class CombinedHash + : TlsHandshakeHash + { + protected TlsContext mContext; + protected IDigest mMd5; + protected IDigest mSha1; + + internal CombinedHash() + { + this.mMd5 = TlsUtilities.CreateHash(HashAlgorithm.md5); + this.mSha1 = TlsUtilities.CreateHash(HashAlgorithm.sha1); + } + + internal CombinedHash(CombinedHash t) + { + this.mContext = t.mContext; + this.mMd5 = TlsUtilities.CloneHash(HashAlgorithm.md5, t.mMd5); + this.mSha1 = TlsUtilities.CloneHash(HashAlgorithm.sha1, t.mSha1); + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual TlsHandshakeHash NotifyPrfDetermined() + { + return this; + } + + public virtual void TrackHashAlgorithm(byte hashAlgorithm) + { + throw new InvalidOperationException("CombinedHash only supports calculating the legacy PRF for handshake hash"); + } + + public virtual void SealHashAlgorithms() + { + } + + public virtual TlsHandshakeHash StopTracking() + { + return new CombinedHash(this); + } + + public virtual IDigest ForkPrfHash() + { + return new CombinedHash(this); + } + + public virtual byte[] GetFinalHash(byte hashAlgorithm) + { + throw new InvalidOperationException("CombinedHash doesn't support multiple hashes"); + } + + public virtual string AlgorithmName + { + get { return mMd5.AlgorithmName + " and " + mSha1.AlgorithmName; } + } + + public virtual int GetByteLength() + { + return System.Math.Max(mMd5.GetByteLength(), mSha1.GetByteLength()); + } + + public virtual int GetDigestSize() + { + return mMd5.GetDigestSize() + mSha1.GetDigestSize(); + } + + public virtual void Update(byte input) + { + mMd5.Update(input); + mSha1.Update(input); + } + + /** + * @see org.bouncycastle.crypto.Digest#update(byte[], int, int) + */ + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + mMd5.BlockUpdate(input, inOff, len); + mSha1.BlockUpdate(input, inOff, len); + } + + /** + * @see org.bouncycastle.crypto.Digest#doFinal(byte[], int) + */ + public virtual int DoFinal(byte[] output, int outOff) + { + if (mContext != null && TlsUtilities.IsSsl(mContext)) + { + Ssl3Complete(mMd5, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 48); + Ssl3Complete(mSha1, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 40); + } + + int i1 = mMd5.DoFinal(output, outOff); + int i2 = mSha1.DoFinal(output, outOff + i1); + return i1 + i2; + } + + /** + * @see org.bouncycastle.crypto.Digest#reset() + */ + public virtual void Reset() + { + mMd5.Reset(); + mSha1.Reset(); + } + + protected virtual void Ssl3Complete(IDigest d, byte[] ipad, byte[] opad, int padLength) + { + byte[] master_secret = mContext.SecurityParameters.masterSecret; + + d.BlockUpdate(master_secret, 0, master_secret.Length); + d.BlockUpdate(ipad, 0, padLength); + + byte[] tmp = DigestUtilities.DoFinal(d); + + d.BlockUpdate(master_secret, 0, master_secret.Length); + d.BlockUpdate(opad, 0, padLength); + d.BlockUpdate(tmp, 0, tmp.Length); + } + } } diff --git a/crypto/src/crypto/tls/DeferredHash.cs b/crypto/src/crypto/tls/DeferredHash.cs new file mode 100644 index 000000000..c2d5ab5b6 --- /dev/null +++ b/crypto/src/crypto/tls/DeferredHash.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * Buffers input until the hash algorithm is determined. + */ + internal class DeferredHash + : TlsHandshakeHash + { + protected const int BUFFERING_HASH_LIMIT = 4; + + protected TlsContext mContext; + + private DigestInputBuffer mBuf; + private IDictionary mHashes; + private int mPrfHashAlgorithm; + + internal DeferredHash() + { + this.mBuf = new DigestInputBuffer(); + this.mHashes = Platform.CreateHashtable(); + this.mPrfHashAlgorithm = -1; + } + + private DeferredHash(byte prfHashAlgorithm, IDigest prfHash) + { + this.mBuf = null; + this.mHashes = Platform.CreateHashtable(); + this.mPrfHashAlgorithm = prfHashAlgorithm; + mHashes[prfHashAlgorithm] = prfHash; + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual TlsHandshakeHash NotifyPrfDetermined() + { + int prfAlgorithm = mContext.SecurityParameters.PrfAlgorithm; + if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy) + { + CombinedHash legacyHash = new CombinedHash(); + legacyHash.Init(mContext); + mBuf.UpdateDigest(legacyHash); + return legacyHash.NotifyPrfDetermined(); + } + + this.mPrfHashAlgorithm = TlsUtilities.GetHashAlgorithmForPrfAlgorithm(prfAlgorithm); + + CheckTrackingHash((byte)mPrfHashAlgorithm); + + return this; + } + + public virtual void TrackHashAlgorithm(byte hashAlgorithm) + { + if (mBuf == null) + throw new InvalidOperationException("Too late to track more hash algorithms"); + + CheckTrackingHash(hashAlgorithm); + } + + public virtual void SealHashAlgorithms() + { + CheckStopBuffering(); + } + + public virtual TlsHandshakeHash StopTracking() + { + IDigest prfHash = TlsUtilities.CloneHash((byte)mPrfHashAlgorithm, (IDigest)mHashes[mPrfHashAlgorithm]); + if (mBuf != null) + { + mBuf.UpdateDigest(prfHash); + } + DeferredHash result = new DeferredHash((byte)mPrfHashAlgorithm, prfHash); + result.Init(mContext); + return result; + } + + public virtual IDigest ForkPrfHash() + { + CheckStopBuffering(); + + if (mBuf != null) + { + IDigest prfHash = TlsUtilities.CreateHash((byte)mPrfHashAlgorithm); + mBuf.UpdateDigest(prfHash); + return prfHash; + } + + return TlsUtilities.CloneHash((byte)mPrfHashAlgorithm, (IDigest)mHashes[mPrfHashAlgorithm]); + } + + public virtual byte[] GetFinalHash(byte hashAlgorithm) + { + IDigest d = (IDigest)mHashes[hashAlgorithm]; + if (d == null) + throw new InvalidOperationException("HashAlgorithm " + hashAlgorithm + " is not being tracked"); + + d = TlsUtilities.CloneHash(hashAlgorithm, d); + if (mBuf != null) + { + mBuf.UpdateDigest(d); + } + + return DigestUtilities.DoFinal(d); + } + + public virtual string AlgorithmName + { + get { throw new InvalidOperationException("Use Fork() to get a definite IDigest"); } + } + + public virtual int GetByteLength() + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual int GetDigestSize() + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual void Update(byte input) + { + if (mBuf != null) + { + mBuf.WriteByte(input); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.Update(input); + } + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + if (mBuf != null) + { + mBuf.Write(input, inOff, len); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.BlockUpdate(input, inOff, len); + } + } + + public virtual int DoFinal(byte[] output, int outOff) + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual void Reset() + { + if (mBuf != null) + { + mBuf.SetLength(0); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.Reset(); + } + } + + protected virtual void CheckStopBuffering() + { + if (mBuf != null && mHashes.Count <= BUFFERING_HASH_LIMIT) + { + foreach (IDigest hash in mHashes.Values) + { + mBuf.UpdateDigest(hash); + } + + this.mBuf = null; + } + } + + protected virtual void CheckTrackingHash(byte hashAlgorithm) + { + if (!mHashes.Contains(hashAlgorithm)) + { + IDigest hash = TlsUtilities.CreateHash(hashAlgorithm); + mHashes[hashAlgorithm] = hash; + } + } + } +} diff --git a/crypto/src/crypto/tls/DigitallySigned.cs b/crypto/src/crypto/tls/DigitallySigned.cs new file mode 100644 index 000000000..8b7344fd9 --- /dev/null +++ b/crypto/src/crypto/tls/DigitallySigned.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DigitallySigned + { + protected readonly SignatureAndHashAlgorithm mAlgorithm; + protected readonly byte[] mSignature; + + public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature) + { + if (signature == null) + throw new ArgumentNullException("signature"); + + this.mAlgorithm = algorithm; + this.mSignature = signature; + } + + /** + * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2). + */ + public virtual SignatureAndHashAlgorithm Algorithm + { + get { return mAlgorithm; } + } + + public virtual byte[] Signature + { + get { return mSignature; } + } + + /** + * Encode this {@link DigitallySigned} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + if (mAlgorithm != null) + { + mAlgorithm.Encode(output); + } + TlsUtilities.WriteOpaque16(mSignature, output); + } + + /** + * Parse a {@link DigitallySigned} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link DigitallySigned} object. + * @throws IOException + */ + public static DigitallySigned Parse(TlsContext context, Stream input) + { + SignatureAndHashAlgorithm algorithm = null; + if (TlsUtilities.IsTlsV12(context)) + { + algorithm = SignatureAndHashAlgorithm.Parse(input); + } + byte[] signature = TlsUtilities.ReadOpaque16(input); + return new DigitallySigned(algorithm, signature); + } + } +} diff --git a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs b/crypto/src/crypto/tls/LegacyTlsAuthentication.cs index bce31c0b0..0c0362c4b 100644 --- a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs +++ b/crypto/src/crypto/tls/LegacyTlsAuthentication.cs @@ -7,7 +7,7 @@ namespace Org.BouncyCastle.Crypto.Tls /// </summary> [Obsolete] public class LegacyTlsAuthentication - : TlsAuthentication + : ServerOnlyTlsAuthentication { protected ICertificateVerifyer verifyer; @@ -16,15 +16,10 @@ namespace Org.BouncyCastle.Crypto.Tls this.verifyer = verifyer; } - public virtual void NotifyServerCertificate(Certificate serverCertificate) + public override void NotifyServerCertificate(Certificate serverCertificate) { if (!this.verifyer.IsValid(serverCertificate.GetCertificateList())) throw new TlsFatalAlert(AlertDescription.user_canceled); } - - public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) - { - return null; - } } } diff --git a/crypto/src/crypto/tls/ServerDHParams.cs b/crypto/src/crypto/tls/ServerDHParams.cs new file mode 100644 index 000000000..381858854 --- /dev/null +++ b/crypto/src/crypto/tls/ServerDHParams.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerDHParams + { + protected readonly DHPublicKeyParameters mPublicKey; + + public ServerDHParams(DHPublicKeyParameters publicKey) + { + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + + this.mPublicKey = publicKey; + } + + public virtual DHPublicKeyParameters PublicKey + { + get { return mPublicKey; } + } + + /** + * Encode this {@link ServerDHParams} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + DHParameters dhParameters = mPublicKey.Parameters; + BigInteger Ys = mPublicKey.Y; + + TlsDHUtilities.WriteDHParameter(dhParameters.P, output); + TlsDHUtilities.WriteDHParameter(dhParameters.G, output); + TlsDHUtilities.WriteDHParameter(Ys, output); + } + + /** + * Parse a {@link ServerDHParams} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerDHParams} object. + * @throws IOException + */ + public static ServerDHParams Parse(Stream input) + { + BigInteger p = TlsDHUtilities.ReadDHParameter(input); + BigInteger g = TlsDHUtilities.ReadDHParameter(input); + BigInteger Ys = TlsDHUtilities.ReadDHParameter(input); + + return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + } + } +} diff --git a/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs new file mode 100644 index 000000000..485889709 --- /dev/null +++ b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs @@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class ServerOnlyTlsAuthentication + : TlsAuthentication + { + public abstract void NotifyServerCertificate(Certificate serverCertificate); + + public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + return null; + } + } +} diff --git a/crypto/src/crypto/tls/TlsDHUtilities.cs b/crypto/src/crypto/tls/TlsDHUtilities.cs index b5deb8b84..477c3ebac 100644 --- a/crypto/src/crypto/tls/TlsDHUtilities.cs +++ b/crypto/src/crypto/tls/TlsDHUtilities.cs @@ -12,6 +12,14 @@ namespace Org.BouncyCastle.Crypto.Tls { public abstract class TlsDHUtilities { + internal static readonly BigInteger One = BigInteger.One; + internal static readonly BigInteger Two = BigInteger.Two; + + public static bool AreCompatibleParameters(DHParameters a, DHParameters b) + { + return a.P.Equals(b.P) && a.G.Equals(b.G); + } + public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey) { @@ -36,15 +44,23 @@ namespace Org.BouncyCastle.Crypto.Tls public static DHPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, DHParameters dhParams, Stream output) { - AsymmetricCipherKeyPair dhAgreeClientKeyPair = GenerateDHKeyPair(random, dhParams); - DHPrivateKeyParameters dhAgreeClientPrivateKey = - (DHPrivateKeyParameters)dhAgreeClientKeyPair.Private; + AsymmetricCipherKeyPair kp = GenerateDHKeyPair(random, dhParams); + + DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.Public; + WriteDHParameter(dhPublic.Y, output); + + return (DHPrivateKeyParameters)kp.Private; + } + + public static DHPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, + DHParameters dhParams, Stream output) + { + AsymmetricCipherKeyPair kp = GenerateDHKeyPair(random, dhParams); - BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.Public).Y; - byte[] keData = BigIntegers.AsUnsignedByteArray(Yc); - TlsUtilities.WriteOpaque16(keData, output); + DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.Public; + new ServerDHParams(dhPublic).Encode(output); - return dhAgreeClientPrivateKey; + return (DHPrivateKeyParameters)kp.Private; } public static DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key) @@ -58,11 +74,11 @@ namespace Org.BouncyCastle.Crypto.Tls { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - if (g.CompareTo(BigInteger.Two) < 0 || g.CompareTo(p.Subtract(BigInteger.Two)) > 0) + if (g.CompareTo(Two) < 0 || g.CompareTo(p.Subtract(Two)) > 0) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - if (Y.CompareTo(BigInteger.Two) < 0 || Y.CompareTo(p.Subtract(BigInteger.One)) > 0) + if (Y.CompareTo(Two) < 0 || Y.CompareTo(p.Subtract(One)) > 0) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } @@ -71,5 +87,15 @@ namespace Org.BouncyCastle.Crypto.Tls return key; } + + public static BigInteger ReadDHParameter(Stream input) + { + return new BigInteger(1, TlsUtilities.ReadOpaque16(input)); + } + + public static void WriteDHParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output); + } } -} \ No newline at end of file +} diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs index 399438879..889c6932b 100644 --- a/crypto/src/crypto/tls/TlsEccUtilities.cs +++ b/crypto/src/crypto/tls/TlsEccUtilities.cs @@ -16,7 +16,7 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public class TlsEccUtilities + public abstract class TlsEccUtilities { private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1", "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs index ca1d4183b..8876911e6 100644 --- a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs +++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs @@ -6,7 +6,7 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public class TlsExtensionsUtilities + public abstract class TlsExtensionsUtilities { public static IDictionary EnsureExtensionsInitialised(IDictionary extensions) { diff --git a/crypto/src/crypto/tls/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs index ada08ef9f..bbb6ac280 100644 --- a/crypto/src/crypto/tls/TlsSrpUtilities.cs +++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs @@ -4,7 +4,7 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public class TlsSrpUtilities + public abstract class TlsSrpUtilities { public static void AddSrpExtension(IDictionary extensions, byte[] identity) { diff --git a/crypto/src/crypto/tls/TlsSrtpUtilities.cs b/crypto/src/crypto/tls/TlsSrtpUtilities.cs new file mode 100644 index 000000000..626c0e3a4 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrtpUtilities.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5764 DTLS Extension to Establish Keys for SRTP. + */ + public abstract class TlsSRTPUtils + { + public static void AddUseSrtpExtension(IDictionary extensions, UseSrtpData useSRTPData) + { + extensions[ExtensionType.use_srtp] = CreateUseSrtpExtension(useSRTPData); + } + + public static UseSrtpData GetUseSrtpExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.use_srtp); + return extensionData == null ? null : ReadUseSrtpExtension(extensionData); + } + + public static byte[] CreateUseSrtpExtension(UseSrtpData useSrtpData) + { + if (useSrtpData == null) + throw new ArgumentNullException("useSrtpData"); + + MemoryStream buf = new MemoryStream(); + + // SRTPProtectionProfiles + TlsUtilities.WriteUint16ArrayWithUint16Length(useSrtpData.ProtectionProfiles, buf); + + // srtp_mki + TlsUtilities.WriteOpaque8(useSrtpData.Mki, buf); + + return buf.ToArray(); + } + + public static UseSrtpData ReadUseSrtpExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, true); + + // SRTPProtectionProfiles + int length = TlsUtilities.ReadUint16(buf); + if (length < 2 || (length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + int[] protectionProfiles = TlsUtilities.ReadUint16Array(length / 2, buf); + + // srtp_mki + byte[] mki = TlsUtilities.ReadOpaque8(buf); + + TlsProtocol.AssertEmpty(buf); + + return new UseSrtpData(protectionProfiles, mki); + } + } +} diff --git a/crypto/src/crypto/tls/TlsStream.cs b/crypto/src/crypto/tls/TlsStream.cs index fe24ad3ed..84b901d6e 100644 --- a/crypto/src/crypto/tls/TlsStream.cs +++ b/crypto/src/crypto/tls/TlsStream.cs @@ -3,84 +3,84 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsStream - : Stream - { - private readonly TlsProtocolHandler handler; + internal class TlsStream + : Stream + { + private readonly TlsProtocolHandler handler; - internal TlsStream( - TlsProtocolHandler handler) - { - this.handler = handler; - } + internal TlsStream( + TlsProtocolHandler handler) + { + this.handler = handler; + } - public override bool CanRead - { - get { return !handler.IsClosed; } - } + public override bool CanRead + { + get { return !handler.IsClosed; } + } - public override bool CanSeek - { - get { return false; } - } + public override bool CanSeek + { + get { return false; } + } - public override bool CanWrite - { - get { return !handler.IsClosed; } - } + public override bool CanWrite + { + get { return !handler.IsClosed; } + } - public override void Close() - { - handler.Close(); - } + public override void Close() + { + handler.Close(); + } - public override void Flush() - { - handler.Flush(); - } + public override void Flush() + { + handler.Flush(); + } public override long Length - { - get { throw new NotSupportedException(); } - } + { + get { throw new NotSupportedException(); } + } - public override long Position + public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } - public override int Read(byte[] buf, int off, int len) - { - return this.handler.ReadApplicationData(buf, off, len); - } + public override int Read(byte[] buf, int off, int len) + { + return this.handler.ReadApplicationData(buf, off, len); + } - public override int ReadByte() - { - byte[] buf = new byte[1]; - if (this.Read(buf, 0, 1) <= 0) - return -1; - return buf[0]; - } + public override int ReadByte() + { + byte[] buf = new byte[1]; + if (this.Read(buf, 0, 1) <= 0) + return -1; + return buf[0]; + } - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } public override void SetLength(long value) - { - throw new NotSupportedException(); - } + { + throw new NotSupportedException(); + } - public override void Write(byte[] buf, int off, int len) - { - this.handler.WriteData(buf, off, len); - } + public override void Write(byte[] buf, int off, int len) + { + this.handler.WriteData(buf, off, len); + } - public override void WriteByte(byte b) - { - this.handler.WriteData(new byte[] { b }, 0, 1); - } - } + public override void WriteByte(byte b) + { + this.handler.WriteData(new byte[] { b }, 0, 1); + } + } } diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index 3fc6c7df1..f530b01a6 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -18,7 +18,7 @@ using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { /// <remarks>Some helper functions for MicroTLS.</remarks> - public class TlsUtilities + public abstract class TlsUtilities { public static readonly byte[] EmptyBytes = new byte[0]; diff --git a/crypto/src/crypto/tls/UseSrtpData.cs b/crypto/src/crypto/tls/UseSrtpData.cs new file mode 100644 index 000000000..fe8f8accb --- /dev/null +++ b/crypto/src/crypto/tls/UseSrtpData.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5764 4.1.1 + */ + public class UseSrtpData + { + protected readonly int[] mProtectionProfiles; + protected readonly byte[] mMki; + + /** + * @param protectionProfiles see {@link SrtpProtectionProfile} for valid constants. + * @param mki valid lengths from 0 to 255. + */ + public UseSrtpData(int[] protectionProfiles, byte[] mki) + { + if (protectionProfiles == null || protectionProfiles.Length < 1 + || protectionProfiles.Length >= (1 << 15)) + { + throw new ArgumentException("must have length from 1 to (2^15 - 1)", "protectionProfiles"); + } + + if (mki == null) + { + mki = TlsUtilities.EmptyBytes; + } + else if (mki.Length > 255) + { + throw new ArgumentException("cannot be longer than 255 bytes", "mki"); + } + + this.mProtectionProfiles = protectionProfiles; + this.mMki = mki; + } + + /** + * @return see {@link SrtpProtectionProfile} for valid constants. + */ + public virtual int[] ProtectionProfiles + { + get { return mProtectionProfiles; } + } + + /** + * @return valid lengths from 0 to 255. + */ + public virtual byte[] Mki + { + get { return mMki; } + } + } +} |