diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index 2e77ca2a9..cf039d7fe 100644
--- a/crypto/src/asn1/Asn1Set.cs
+++ b/crypto/src/asn1/Asn1Set.cs
@@ -278,67 +278,30 @@ namespace Org.BouncyCastle.Asn1
return encObj;
}
- /**
- * return true if a <= b (arrays are assumed padded with zeros).
- */
- private bool LessThanOrEqual(
- byte[] a,
- byte[] b)
+ protected internal void Sort()
{
- int len = System.Math.Min(a.Length, b.Length);
- for (int i = 0; i != len; ++i)
+ if (_set.Count < 2)
+ return;
+
+ Asn1Encodable[] items = new Asn1Encodable[_set.Count];
+ byte[][] keys = new byte[_set.Count][];
+
+ for (int i = 0; i < _set.Count; ++i)
{
- if (a[i] != b[i])
- {
- return a[i] < b[i];
- }
+ Asn1Encodable item = (Asn1Encodable)_set[i];
+ items[i] = item;
+ keys[i] = item.GetEncoded(Asn1Encodable.Der);
}
- return len == a.Length;
- }
- protected internal void Sort()
- {
- if (_set.Count > 1)
- {
- bool swapped = true;
- int lastSwap = _set.Count - 1;
+ Array.Sort(keys, items, new DerComparer());
- while (swapped)
- {
- int index = 0;
- int swapIndex = 0;
- byte[] a = ((Asn1Encodable) _set[0]).GetEncoded();
-
- swapped = false;
-
- while (index != lastSwap)
- {
- byte[] b = ((Asn1Encodable) _set[index + 1]).GetEncoded();
-
- if (LessThanOrEqual(a, b))
- {
- a = b;
- }
- else
- {
- object o = _set[index];
- _set[index] = _set[index + 1];
- _set[index + 1] = o;
-
- swapped = true;
- swapIndex = index;
- }
-
- index++;
- }
-
- lastSwap = swapIndex;
- }
+ for (int i = 0; i < _set.Count; ++i)
+ {
+ _set[i] = items[i];
}
}
- protected internal void AddObject(
- Asn1Encodable obj)
+ protected internal void AddObject(Asn1Encodable obj)
{
_set.Add(obj);
}
@@ -347,5 +310,36 @@ namespace Org.BouncyCastle.Asn1
{
return CollectionUtilities.ToString(_set);
}
+
+ private class DerComparer
+ : IComparer
+ {
+ public int Compare(object x, object y)
+ {
+ byte[] a = (byte[])x, b = (byte[])y;
+ int len = System.Math.Min(a.Length, b.Length);
+ for (int i = 0; i != len; ++i)
+ {
+ byte ai = a[i], bi = b[i];
+ if (ai != bi)
+ return ai < bi ? -1 : 1;
+ }
+ if (a.Length > b.Length)
+ return AllZeroesFrom(a, len) ? 0 : 1;
+ if (a.Length < b.Length)
+ return AllZeroesFrom(b, len) ? 0 : -1;
+ return 0;
+ }
+
+ private bool AllZeroesFrom(byte[] bs, int pos)
+ {
+ while (pos < bs.Length)
+ {
+ if (bs[pos++] != 0)
+ return false;
+ }
+ return true;
+ }
+ }
}
}
diff --git a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
index a63595d54..404277ba6 100644
--- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
+++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
@@ -91,7 +91,7 @@ namespace Org.BouncyCastle.Asn1.Pkcs
{
return ParsePrivateKey();
}
- catch (IOException e)
+ catch (IOException)
{
throw new InvalidOperationException("unable to parse private key");
}
diff --git a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
index dbb07c744..721299105 100644
--- a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
+++ b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
@@ -18,7 +18,21 @@ namespace Org.BouncyCastle.Asn1.Pkcs
private readonly BigInteger exponent2;
private readonly BigInteger coefficient;
- public RsaPrivateKeyStructure(
+ public static RsaPrivateKeyStructure GetInstance(Asn1TaggedObject obj, bool isExplicit)
+ {
+ return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+ }
+
+ public static RsaPrivateKeyStructure GetInstance(object obj)
+ {
+ if (obj == null)
+ return null;
+ if (obj is RsaPrivateKeyStructure)
+ return (RsaPrivateKeyStructure)obj;
+ return new RsaPrivateKeyStructure(Asn1Sequence.GetInstance(obj));
+ }
+
+ public RsaPrivateKeyStructure(
BigInteger modulus,
BigInteger publicExponent,
BigInteger privateExponent,
@@ -38,64 +52,65 @@ namespace Org.BouncyCastle.Asn1.Pkcs
this.coefficient = coefficient;
}
- public RsaPrivateKeyStructure(
+ [Obsolete("Use 'GetInstance' method(s) instead")]
+ public RsaPrivateKeyStructure(
Asn1Sequence seq)
{
- BigInteger version = ((DerInteger) seq[0]).Value;
- if (version.IntValue != 0)
+ BigInteger version = ((DerInteger) seq[0]).Value;
+ if (version.IntValue != 0)
throw new ArgumentException("wrong version for RSA private key");
- modulus = ((DerInteger) seq[1]).Value;
- publicExponent = ((DerInteger) seq[2]).Value;
- privateExponent = ((DerInteger) seq[3]).Value;
- prime1 = ((DerInteger) seq[4]).Value;
- prime2 = ((DerInteger) seq[5]).Value;
- exponent1 = ((DerInteger) seq[6]).Value;
- exponent2 = ((DerInteger) seq[7]).Value;
- coefficient = ((DerInteger) seq[8]).Value;
- }
-
- public BigInteger Modulus
- {
- get { return modulus; }
- }
-
- public BigInteger PublicExponent
- {
- get { return publicExponent; }
- }
-
- public BigInteger PrivateExponent
- {
- get { return privateExponent; }
- }
-
- public BigInteger Prime1
- {
- get { return prime1; }
- }
-
- public BigInteger Prime2
- {
- get { return prime2; }
- }
-
- public BigInteger Exponent1
- {
- get { return exponent1; }
- }
-
- public BigInteger Exponent2
- {
- get { return exponent2; }
- }
-
- public BigInteger Coefficient
- {
- get { return coefficient; }
- }
-
- /**
+ modulus = ((DerInteger) seq[1]).Value;
+ publicExponent = ((DerInteger) seq[2]).Value;
+ privateExponent = ((DerInteger) seq[3]).Value;
+ prime1 = ((DerInteger) seq[4]).Value;
+ prime2 = ((DerInteger) seq[5]).Value;
+ exponent1 = ((DerInteger) seq[6]).Value;
+ exponent2 = ((DerInteger) seq[7]).Value;
+ coefficient = ((DerInteger) seq[8]).Value;
+ }
+
+ public BigInteger Modulus
+ {
+ get { return modulus; }
+ }
+
+ public BigInteger PublicExponent
+ {
+ get { return publicExponent; }
+ }
+
+ public BigInteger PrivateExponent
+ {
+ get { return privateExponent; }
+ }
+
+ public BigInteger Prime1
+ {
+ get { return prime1; }
+ }
+
+ public BigInteger Prime2
+ {
+ get { return prime2; }
+ }
+
+ public BigInteger Exponent1
+ {
+ get { return exponent1; }
+ }
+
+ public BigInteger Exponent2
+ {
+ get { return exponent2; }
+ }
+
+ public BigInteger Coefficient
+ {
+ get { return coefficient; }
+ }
+
+ /**
* This outputs the key in Pkcs1v2 format.
* <pre>
* RsaPrivateKey ::= Sequence {
@@ -116,16 +131,16 @@ namespace Org.BouncyCastle.Asn1.Pkcs
*/
public override Asn1Object ToAsn1Object()
{
- return new DerSequence(
- new DerInteger(0), // version
- new DerInteger(Modulus),
- new DerInteger(PublicExponent),
- new DerInteger(PrivateExponent),
- new DerInteger(Prime1),
- new DerInteger(Prime2),
- new DerInteger(Exponent1),
- new DerInteger(Exponent2),
- new DerInteger(Coefficient));
+ return new DerSequence(
+ new DerInteger(0), // version
+ new DerInteger(Modulus),
+ new DerInteger(PublicExponent),
+ new DerInteger(PrivateExponent),
+ new DerInteger(Prime1),
+ new DerInteger(Prime2),
+ new DerInteger(Exponent1),
+ new DerInteger(Exponent2),
+ new DerInteger(Coefficient));
}
}
}
diff --git a/crypto/src/crypto/agreement/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs
new file mode 100644
index 000000000..93b65af98
--- /dev/null
+++ b/crypto/src/crypto/agreement/DHStandardGroups.cs
@@ -0,0 +1,206 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /// <summary>Standard Diffie-Hellman groups from various IETF specifications.</summary>
+ public class DHStandardGroups
+ {
+ private static BigInteger FromHex(string hex)
+ {
+ return new BigInteger(1, Hex.Decode(hex));
+ }
+
+ private static DHParameters FromPG(string hexP, string hexG)
+ {
+ return new DHParameters(FromHex(hexP), FromHex(hexG));
+ }
+
+ private static DHParameters FromPGQ(string hexP, string hexG, string hexQ)
+ {
+ return new DHParameters(FromHex(hexP), FromHex(hexG), FromHex(hexQ));
+ }
+
+ /*
+ * RFC 2409
+ */
+ private static readonly string rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF";
+ private static readonly string rfc2409_768_g = "02";
+ public static readonly DHParameters rfc2409_768 = FromPG(rfc2409_768_p, rfc2409_768_g);
+
+ private static readonly string rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ + "FFFFFFFFFFFFFFFF";
+ private static readonly string rfc2409_1024_g = "02";
+ public static readonly DHParameters rfc2409_1024 = FromPG(rfc2409_1024_p, rfc2409_1024_g);
+
+ /*
+ * RFC 3526
+ */
+ private static readonly string rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_1536_g = "02";
+ public static readonly DHParameters rfc3526_1536 = FromPG(rfc3526_1536_p, rfc3526_1536_g);
+
+ private static readonly string rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_2048_g = "02";
+ public static readonly DHParameters rfc3526_2048 = FromPG(rfc3526_2048_p, rfc3526_2048_g);
+
+ private static readonly string rfc3526_3072_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_3072_g = "02";
+ public static readonly DHParameters rfc3526_3072 = FromPG(rfc3526_3072_p, rfc3526_3072_g);
+
+ private static readonly string rfc3526_4096_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ + "FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_4096_g = "02";
+ public static readonly DHParameters rfc3526_4096 = FromPG(rfc3526_4096_p, rfc3526_4096_g);
+
+ private static readonly string rfc3526_6144_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ + "6DCC4024FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_6144_g = "02";
+ public static readonly DHParameters rfc3526_6144 = FromPG(rfc3526_6144_p, rfc3526_6144_g);
+
+ private static readonly string rfc3526_8192_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
+ + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
+ + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
+ + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
+ + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
+ + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"
+ + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"
+ + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"
+ + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"
+ + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_8192_g = "02";
+ public static readonly DHParameters rfc3526_8192 = FromPG(rfc3526_8192_p, rfc3526_8192_g);
+
+ /*
+ * RFC 4306
+ */
+ public static readonly DHParameters rfc4306_768 = rfc2409_768;
+ public static readonly DHParameters rfc4306_1024 = rfc2409_1024;
+
+ /*
+ * RFC 5114
+ */
+ private static readonly string rfc5114_1024_160_p = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
+ + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
+ + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
+ + "DF1FB2BC2E4A4371";
+ private static readonly string rfc5114_1024_160_g = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
+ + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
+ + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
+ + "855E6EEB22B3B2E5";
+ private static readonly string rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353";
+ public static readonly DHParameters rfc5114_1024_160 = FromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g,
+ rfc5114_1024_160_q);
+
+ private static readonly string rfc5114_2048_224_p = "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1"
+ + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212"
+ + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708"
+ + "B3BF8A317091883681286130BC8985DB1602E714415D9330" + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D"
+ + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763"
+ + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" + "CF9DE5384E71B81C0AC4DFFE0C10E64F";
+ private static readonly string rfc5114_2048_224_g = "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF"
+ + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA" + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7"
+ + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A" + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE"
+ + "F180EB34118E98D119529A45D6F834566E3025E316A330EF" + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB"
+ + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"
+ + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA";
+ private static readonly string rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB";
+ public static readonly DHParameters rfc5114_2048_224 = FromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g,
+ rfc5114_2048_224_q);
+
+ private static readonly string rfc5114_2048_256_p = "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2"
+ + "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30" + "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD"
+ + "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B" + "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C"
+ + "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E" + "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9"
+ + "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026" + "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3"
+ + "75F26375D7014103A4B54330C198AF126116D2276E11715F" + "693877FAD7EF09CADB094AE91E1A1597";
+ private static readonly string rfc5114_2048_256_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054"
+ + "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555" + "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18"
+ + "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83"
+ + "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55" + "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14"
+ + "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6"
+ + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659";
+ private static readonly string rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B"
+ + "A308B0FE64F5FBD3";
+ public static readonly DHParameters rfc5114_2048_256 = FromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g,
+ rfc5114_2048_256_q);
+
+ /*
+ * RFC 5996
+ */
+ public static readonly DHParameters rfc5996_768 = rfc4306_768;
+ public static readonly DHParameters rfc5996_1024 = rfc4306_1024;
+ }
+}
diff --git a/crypto/src/crypto/engines/IesEngine.cs b/crypto/src/crypto/engines/IesEngine.cs
index 6520c86f8..70df3077c 100644
--- a/crypto/src/crypto/engines/IesEngine.cs
+++ b/crypto/src/crypto/engines/IesEngine.cs
@@ -133,7 +133,7 @@ namespace Org.BouncyCastle.Crypto.Engines
inOff += inLen;
- byte[] T1 = Arrays.Copy(in_enc, inOff, macBuf.Length);
+ byte[] T1 = Arrays.CopyOfRange(in_enc, inOff, inOff + macBuf.Length);
if (!Arrays.ConstantTimeAreEqual(T1, macBuf))
throw (new InvalidCipherTextException("Invalid MAC."));
diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs
index 2d453b6ad..1a951ca04 100644
--- a/crypto/src/crypto/macs/Poly1305.cs
+++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -7,266 +7,285 @@ using Org.BouncyCastle.Crypto.Utilities;
namespace Org.BouncyCastle.Crypto.Macs
{
- /// <summary>
- /// Poly1305 message authentication code, designed by D. J. Bernstein.
- /// </summary>
- /// <remarks>
- /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
- /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
- /// effective key bits) used in the authenticator.
- ///
- /// The polynomial calculation in this implementation is adapted from the public domain <a
- /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
- /// by Andrew M (@floodyberry).
- /// </remarks>
- /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
- public class Poly1305
- : IMac
- {
- private const int BLOCK_SIZE = 16;
-
- private readonly IBlockCipher cipher;
-
- private readonly byte[] singleByte = new byte[1];
-
- // Initialised state
-
- /** Polynomial key */
- private uint r0, r1, r2, r3, r4;
-
- /** Precomputed 5 * r[1..4] */
- private uint s1, s2, s3, s4;
-
- /** Encrypted nonce */
- private uint k0, k1, k2, k3;
-
- // Accumulating state
-
- /** Current block of buffered input */
- private byte[] currentBlock = new byte[BLOCK_SIZE];
-
- /** Current offset in input buffer */
- private int currentBlockOffset = 0;
-
- /** Polynomial accumulator */
- private uint h0, h1, h2, h3, h4;
-
- /**
- * Constructs a Poly1305 MAC, using a 128 bit block cipher.
- */
- public Poly1305(IBlockCipher cipher)
- {
- if (cipher.GetBlockSize() != BLOCK_SIZE)
- {
- throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
- }
- this.cipher = cipher;
- }
-
- /// <summary>
- /// Initialises the Poly1305 MAC.
- /// </summary>
- /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
- /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param>
- public void Init(ICipherParameters parameters)
- {
- byte[] nonce;
- byte[] key;
- if ((parameters is ParametersWithIV) && ((ParametersWithIV)parameters).Parameters is KeyParameter)
- {
- nonce = ((ParametersWithIV)parameters).GetIV();
- key = ((KeyParameter)((ParametersWithIV)parameters).Parameters).GetKey();
- }
- else
- {
- throw new ArgumentException("Poly1305 requires a key and and IV.");
- }
-
- setKey(key, nonce);
- Reset();
- }
-
- private void setKey(byte[] key, byte[] nonce)
- {
- if (nonce.Length != BLOCK_SIZE)
- {
- throw new ArgumentException("Poly1305 requires a 128 bit IV.");
- }
- Poly1305KeyGenerator.CheckKey(key);
-
- // Extract r portion of key
- uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0);
- uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4);
- uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8);
- uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12);
-
- r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
- r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
- r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
- r3 = t2 & 0x3f03fff; t3 >>= 8;
- r4 = t3 & 0x00fffff;
-
- // Precompute multipliers
- s1 = r1 * 5;
- s2 = r2 * 5;
- s3 = r3 * 5;
- s4 = r4 * 5;
-
- // Compute encrypted nonce
- byte[] cipherKey = new byte[BLOCK_SIZE];
- Array.Copy(key, 0, cipherKey, 0, cipherKey.Length);
-
- cipher.Init(true, new KeyParameter(cipherKey));
- cipher.ProcessBlock(nonce, 0, cipherKey, 0);
-
- k0 = Pack.LE_To_UInt32(cipherKey, 0);
- k1 = Pack.LE_To_UInt32(cipherKey, 4);
- k2 = Pack.LE_To_UInt32(cipherKey, 8);
- k3 = Pack.LE_To_UInt32(cipherKey, 12);
- }
-
- public string AlgorithmName
- {
- get { return "Poly1305-" + cipher.AlgorithmName; }
- }
-
- public int GetMacSize()
- {
- return BLOCK_SIZE;
- }
-
- public void Update(byte input)
- {
- singleByte[0] = input;
- BlockUpdate(singleByte, 0, 1);
- }
-
- public void BlockUpdate(byte[] input, int inOff, int len)
- {
- int copied = 0;
- while (len > copied)
- {
- if (currentBlockOffset == BLOCK_SIZE)
- {
- processBlock();
- currentBlockOffset = 0;
- }
-
- int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset);
- Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy);
- copied += toCopy;
- currentBlockOffset += toCopy;
- }
-
- }
-
- private void processBlock()
- {
- if (currentBlockOffset < BLOCK_SIZE)
- {
- currentBlock[currentBlockOffset] = 1;
- for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
- {
- currentBlock[i] = 0;
- }
- }
-
- ulong t0 = Pack.LE_To_UInt32(currentBlock, 0);
- ulong t1 = Pack.LE_To_UInt32(currentBlock, 4);
- ulong t2 = Pack.LE_To_UInt32(currentBlock, 8);
- ulong t3 = Pack.LE_To_UInt32(currentBlock, 12);
-
- h0 += (uint)(t0 & 0x3ffffffU);
- h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff);
- h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff);
- h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff);
- h4 += (uint)(t3 >> 8);
-
- if (currentBlockOffset == BLOCK_SIZE)
- {
- h4 += (1 << 24);
- }
-
- ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
- ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
- ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
- ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
- ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
-
- ulong b;
- h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26);
- tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26);
- tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26);
- tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26);
- tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26);
- h0 += (uint)(b * 5);
- }
-
- public int DoFinal(byte[] output, int outOff)
- {
- if (outOff + BLOCK_SIZE > output.Length)
- {
- throw new DataLengthException("Output buffer is too short.");
- }
-
- if (currentBlockOffset > 0)
- {
- // Process padded block
- processBlock();
- }
-
- ulong f0, f1, f2, f3;
-
- uint b = h0 >> 26;
- h0 = h0 & 0x3ffffff;
- h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
- h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
- h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
- h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
- h0 += b * 5;
-
- uint g0, g1, g2, g3, g4;
- g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
- g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
- g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
- g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
- g4 = h4 + b - (1 << 26);
-
- b = (g4 >> 31) - 1;
- uint nb = ~b;
- h0 = (h0 & nb) | (g0 & b);
- h1 = (h1 & nb) | (g1 & b);
- h2 = (h2 & nb) | (g2 & b);
- h3 = (h3 & nb) | (g3 & b);
- h4 = (h4 & nb) | (g4 & b);
-
- f0 = ((h0 ) | (h1 << 26)) + (ulong)k0;
- f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1;
- f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2;
- f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3;
-
- Pack.UInt32_To_LE((uint)f0, output, outOff);
- f1 += (f0 >> 32);
- Pack.UInt32_To_LE((uint)f1, output, outOff + 4);
- f2 += (f1 >> 32);
- Pack.UInt32_To_LE((uint)f2, output, outOff + 8);
- f3 += (f2 >> 32);
- Pack.UInt32_To_LE((uint)f3, output, outOff + 12);
-
- Reset();
- return BLOCK_SIZE;
- }
-
- public void Reset()
- {
- currentBlockOffset = 0;
-
- h0 = h1 = h2 = h3 = h4 = 0;
- }
-
- private static ulong mul32x32_64(uint i1, uint i2)
- {
- return ((ulong)i1) * i2;
- }
- }
+ /// <summary>
+ /// Poly1305 message authentication code, designed by D. J. Bernstein.
+ /// </summary>
+ /// <remarks>
+ /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
+ /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
+ /// effective key bits) used in the authenticator.
+ ///
+ /// The polynomial calculation in this implementation is adapted from the public domain <a
+ /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
+ /// by Andrew M (@floodyberry).
+ /// </remarks>
+ /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
+ public class Poly1305
+ : IMac
+ {
+ private const int BLOCK_SIZE = 16;
+
+ private readonly IBlockCipher cipher;
+
+ private readonly byte[] singleByte = new byte[1];
+
+ // Initialised state
+
+ /** Polynomial key */
+ private uint r0, r1, r2, r3, r4;
+
+ /** Precomputed 5 * r[1..4] */
+ private uint s1, s2, s3, s4;
+
+ /** Encrypted nonce */
+ private uint k0, k1, k2, k3;
+
+ // Accumulating state
+
+ /** Current block of buffered input */
+ private byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ /** Polynomial accumulator */
+ private uint h0, h1, h2, h3, h4;
+
+ /**
+ * Constructs a Poly1305 MAC, where the key passed to init() will be used directly.
+ */
+ public Poly1305()
+ {
+ this.cipher = null;
+ }
+
+ /**
+ * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+ */
+ public Poly1305(IBlockCipher cipher)
+ {
+ if (cipher.GetBlockSize() != BLOCK_SIZE)
+ {
+ throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ /// <summary>
+ /// Initialises the Poly1305 MAC.
+ /// </summary>
+ /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
+ /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param>
+ public void Init(ICipherParameters parameters)
+ {
+ byte[] nonce = null;
+
+ if (cipher != null)
+ {
+ if (!(parameters is ParametersWithIV))
+ throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters");
+
+ ParametersWithIV ivParams = (ParametersWithIV)parameters;
+ nonce = ivParams.GetIV();
+ parameters = ivParams.Parameters;
+ }
+
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("Poly1305 requires a key.");
+
+ KeyParameter keyParams = (KeyParameter)parameters;
+
+ SetKey(keyParams.GetKey(), nonce);
+
+ Reset();
+ }
+
+ private void SetKey(byte[] key, byte[] nonce)
+ {
+ if (cipher != null && (nonce == null || nonce.Length != BLOCK_SIZE))
+ throw new ArgumentException("Poly1305 requires a 128 bit IV.");
+
+ Poly1305KeyGenerator.CheckKey(key);
+
+ // Extract r portion of key
+ uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0);
+ uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4);
+ uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8);
+ uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12);
+
+ r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>= 8;
+ r4 = t3 & 0x00fffff;
+
+ // Precompute multipliers
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ byte[] kBytes;
+ if (cipher == null)
+ {
+ kBytes = key;
+ }
+ else
+ {
+ // Compute encrypted nonce
+ kBytes = new byte[BLOCK_SIZE];
+ cipher.Init(true, new KeyParameter(key, 0, BLOCK_SIZE));
+ cipher.ProcessBlock(nonce, 0, kBytes, 0);
+ }
+
+ k0 = Pack.LE_To_UInt32(kBytes, 0);
+ k1 = Pack.LE_To_UInt32(kBytes, 4);
+ k2 = Pack.LE_To_UInt32(kBytes, 8);
+ k3 = Pack.LE_To_UInt32(kBytes, 12);
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher == null ? "Poly1305" : "Poly1305-" + cipher.AlgorithmName; }
+ }
+
+ public int GetMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void Update(byte input)
+ {
+ singleByte[0] = input;
+ BlockUpdate(singleByte, 0, 1);
+ }
+
+ public void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset);
+ Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+ copied += toCopy;
+ currentBlockOffset += toCopy;
+ }
+
+ }
+
+ private void processBlock()
+ {
+ if (currentBlockOffset < BLOCK_SIZE)
+ {
+ currentBlock[currentBlockOffset] = 1;
+ for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
+ {
+ currentBlock[i] = 0;
+ }
+ }
+
+ ulong t0 = Pack.LE_To_UInt32(currentBlock, 0);
+ ulong t1 = Pack.LE_To_UInt32(currentBlock, 4);
+ ulong t2 = Pack.LE_To_UInt32(currentBlock, 8);
+ ulong t3 = Pack.LE_To_UInt32(currentBlock, 12);
+
+ h0 += (uint)(t0 & 0x3ffffffU);
+ h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff);
+ h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff);
+ h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff);
+ h4 += (uint)(t3 >> 8);
+
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ h4 += (1 << 24);
+ }
+
+ ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ ulong b;
+ h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26);
+ tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26);
+ tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26);
+ tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26);
+ tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26);
+ h0 += (uint)(b * 5);
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ if (outOff + BLOCK_SIZE > output.Length)
+ {
+ throw new DataLengthException("Output buffer is too short.");
+ }
+
+ if (currentBlockOffset > 0)
+ {
+ // Process padded block
+ processBlock();
+ }
+
+ ulong f0, f1, f2, f3;
+
+ uint b = h0 >> 26;
+ h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5;
+
+ uint g0, g1, g2, g3, g4;
+ g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >> 31) - 1;
+ uint nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = ((h0 ) | (h1 << 26)) + (ulong)k0;
+ f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1;
+ f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2;
+ f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3;
+
+ Pack.UInt32_To_LE((uint)f0, output, outOff);
+ f1 += (f0 >> 32);
+ Pack.UInt32_To_LE((uint)f1, output, outOff + 4);
+ f2 += (f1 >> 32);
+ Pack.UInt32_To_LE((uint)f2, output, outOff + 8);
+ f3 += (f2 >> 32);
+ Pack.UInt32_To_LE((uint)f3, output, outOff + 12);
+
+ Reset();
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ currentBlockOffset = 0;
+
+ h0 = h1 = h2 = h3 = h4 = 0;
+ }
+
+ private static ulong mul32x32_64(uint i1, uint i2)
+ {
+ return ((ulong)i1) * i2;
+ }
+ }
}
diff --git a/crypto/src/crypto/signers/DsaSigner.cs b/crypto/src/crypto/signers/DsaSigner.cs
index fc0780a91..bb28addfc 100644
--- a/crypto/src/crypto/signers/DsaSigner.cs
+++ b/crypto/src/crypto/signers/DsaSigner.cs
@@ -1,10 +1,9 @@
using System;
-using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Math.EC;
-using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Crypto;
+
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Signers
{
@@ -15,9 +14,29 @@ namespace Org.BouncyCastle.Crypto.Signers
public class DsaSigner
: IDsa
{
+ protected readonly IDsaKCalculator kCalculator;
+
protected DsaKeyParameters key = null;
protected SecureRandom random = null;
+ /**
+ * Default configuration, random K values.
+ */
+ public DsaSigner()
+ {
+ this.kCalculator = new RandomDsaKCalculator();
+ }
+
+ /**
+ * Configuration with an alternate, possibly deterministic calculator of K.
+ *
+ * @param kCalculator a K value calculator.
+ */
+ public DsaSigner(IDsaKCalculator kCalculator)
+ {
+ this.kCalculator = kCalculator;
+ }
+
public virtual string AlgorithmName
{
get { return "DSA"; }
@@ -50,7 +69,7 @@ namespace Org.BouncyCastle.Crypto.Signers
this.key = (DsaPublicKeyParameters)parameters;
}
- this.random = InitSecureRandom(forSigning, providedRandom);
+ this.random = InitSecureRandom(forSigning && !kCalculator.IsDeterministic, providedRandom);
}
/**
@@ -65,18 +84,22 @@ namespace Org.BouncyCastle.Crypto.Signers
DsaParameters parameters = key.Parameters;
BigInteger q = parameters.Q;
BigInteger m = CalculateE(q, message);
- BigInteger k;
+ BigInteger x = ((DsaPrivateKeyParameters)key).X;
- do
+ if (kCalculator.IsDeterministic)
{
- k = new BigInteger(q.BitLength, random);
+ kCalculator.Init(q, x, message);
}
- while (k.CompareTo(q) >= 0);
+ else
+ {
+ kCalculator.Init(q, random);
+ }
+
+ BigInteger k = kCalculator.NextK();
BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q);
- k = k.ModInverse(q).Multiply(
- m.Add(((DsaPrivateKeyParameters)key).X.Multiply(r)));
+ k = k.ModInverse(q).Multiply(m.Add(x.Multiply(r)));
BigInteger s = k.Mod(q);
diff --git a/crypto/src/crypto/signers/ECDsaSigner.cs b/crypto/src/crypto/signers/ECDsaSigner.cs
index 867520535..9821732c2 100644
--- a/crypto/src/crypto/signers/ECDsaSigner.cs
+++ b/crypto/src/crypto/signers/ECDsaSigner.cs
@@ -1,11 +1,11 @@
using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC.Multiplier;
using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Digests;
-using Org.BouncyCastle.Crypto.Parameters;
namespace Org.BouncyCastle.Crypto.Signers
{
@@ -15,9 +15,29 @@ namespace Org.BouncyCastle.Crypto.Signers
public class ECDsaSigner
: IDsa
{
+ protected readonly IDsaKCalculator kCalculator;
+
protected ECKeyParameters key = null;
protected SecureRandom random = null;
+ /**
+ * Default configuration, random K values.
+ */
+ public ECDsaSigner()
+ {
+ this.kCalculator = new RandomDsaKCalculator();
+ }
+
+ /**
+ * Configuration with an alternate, possibly deterministic calculator of K.
+ *
+ * @param kCalculator a K value calculator.
+ */
+ public ECDsaSigner(IDsaKCalculator kCalculator)
+ {
+ this.kCalculator = kCalculator;
+ }
+
public virtual string AlgorithmName
{
get { return "ECDSA"; }
@@ -50,7 +70,7 @@ namespace Org.BouncyCastle.Crypto.Signers
this.key = (ECPublicKeyParameters)parameters;
}
- this.random = InitSecureRandom(forSigning, providedRandom);
+ this.random = InitSecureRandom(forSigning && !kCalculator.IsDeterministic, providedRandom);
}
// 5.3 pg 28
@@ -68,6 +88,15 @@ namespace Org.BouncyCastle.Crypto.Signers
BigInteger e = CalculateE(n, message);
BigInteger d = ((ECPrivateKeyParameters)key).D;
+ if (kCalculator.IsDeterministic)
+ {
+ kCalculator.Init(n, d, message);
+ }
+ else
+ {
+ kCalculator.Init(n, random);
+ }
+
BigInteger r, s;
ECMultiplier basePointMultiplier = CreateBasePointMultiplier();
@@ -78,11 +107,7 @@ namespace Org.BouncyCastle.Crypto.Signers
BigInteger k;
do // Generate r
{
- do
- {
- k = new BigInteger(n.BitLength, random);
- }
- while (k.SignValue == 0 || k.CompareTo(n) >= 0);
+ k = kCalculator.NextK();
ECPoint p = basePointMultiplier.Multiply(ec.G, k).Normalize();
diff --git a/crypto/src/crypto/signers/GenericSigner.cs b/crypto/src/crypto/signers/GenericSigner.cs
index 1a53eee2b..5035b454d 100644
--- a/crypto/src/crypto/signers/GenericSigner.cs
+++ b/crypto/src/crypto/signers/GenericSigner.cs
@@ -6,124 +6,125 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Signers
{
- public class GenericSigner
- : ISigner
- {
- private readonly IAsymmetricBlockCipher engine;
- private readonly IDigest digest;
- private bool forSigning;
-
- public GenericSigner(
- IAsymmetricBlockCipher engine,
- IDigest digest)
- {
- this.engine = engine;
- this.digest = digest;
- }
-
- public string AlgorithmName
- {
- get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; }
- }
-
- /**
- * initialise the signer for signing or verification.
- *
- * @param forSigning
- * true if for signing, false otherwise
- * @param parameters
- * necessary parameters.
- */
- public void Init(
- bool forSigning,
- ICipherParameters parameters)
- {
- this.forSigning = forSigning;
- AsymmetricKeyParameter k;
-
- if (parameters is ParametersWithRandom)
- {
- k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
- }
- else
- {
- k = (AsymmetricKeyParameter)parameters;
- }
+ public class GenericSigner
+ : ISigner
+ {
+ private readonly IAsymmetricBlockCipher engine;
+ private readonly IDigest digest;
+ private bool forSigning;
+
+ public GenericSigner(
+ IAsymmetricBlockCipher engine,
+ IDigest digest)
+ {
+ this.engine = engine;
+ this.digest = digest;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; }
+ }
+
+ /**
+ * initialise the signer for signing or verification.
+ *
+ * @param forSigning
+ * true if for signing, false otherwise
+ * @param parameters
+ * necessary parameters.
+ */
+ public void Init(bool forSigning, ICipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+
+ AsymmetricKeyParameter k;
+ if (parameters is ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
if (forSigning && !k.IsPrivate)
throw new InvalidKeyException("Signing requires private key.");
- if (!forSigning && k.IsPrivate)
+ if (!forSigning && k.IsPrivate)
throw new InvalidKeyException("Verification requires public key.");
- Reset();
-
- engine.Init(forSigning, parameters);
- }
-
- /**
- * update the internal digest with the byte b
- */
- public void Update(
- byte input)
- {
- digest.Update(input);
- }
-
- /**
- * update the internal digest with the byte array in
- */
- public void BlockUpdate(
- byte[] input,
- int inOff,
- int length)
- {
- digest.BlockUpdate(input, inOff, length);
- }
-
- /**
- * Generate a signature for the message we've been loaded with using the key
- * we were initialised with.
- */
- public byte[] GenerateSignature()
- {
- if (!forSigning)
- throw new InvalidOperationException("GenericSigner not initialised for signature generation.");
-
- byte[] hash = new byte[digest.GetDigestSize()];
- digest.DoFinal(hash, 0);
-
- return engine.ProcessBlock(hash, 0, hash.Length);
- }
-
- /**
- * return true if the internal state represents the signature described in
- * the passed in array.
- */
- public bool VerifySignature(
- byte[] signature)
- {
- if (forSigning)
- throw new InvalidOperationException("GenericSigner not initialised for verification");
-
- byte[] hash = new byte[digest.GetDigestSize()];
- digest.DoFinal(hash, 0);
-
- try
- {
- byte[] sig = engine.ProcessBlock(signature, 0, signature.Length);
-
- return Arrays.ConstantTimeAreEqual(sig, hash);
- }
- catch (Exception)
- {
- return false;
- }
- }
-
- public void Reset()
- {
- digest.Reset();
- }
- }
+ Reset();
+
+ engine.Init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void Update(byte input)
+ {
+ digest.Update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void BlockUpdate(byte[] input, int inOff, int length)
+ {
+ digest.BlockUpdate(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using the key
+ * we were initialised with.
+ */
+ public byte[] GenerateSignature()
+ {
+ if (!forSigning)
+ throw new InvalidOperationException("GenericSigner not initialised for signature generation.");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ return engine.ProcessBlock(hash, 0, hash.Length);
+ }
+
+ /**
+ * return true if the internal state represents the signature described in
+ * the passed in array.
+ */
+ public bool VerifySignature(byte[] signature)
+ {
+ if (forSigning)
+ throw new InvalidOperationException("GenericSigner not initialised for verification");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ try
+ {
+ byte[] sig = engine.ProcessBlock(signature, 0, signature.Length);
+
+ // Extend with leading zeroes to match the digest size, if necessary.
+ if (sig.Length < hash.Length)
+ {
+ byte[] tmp = new byte[hash.Length];
+ Array.Copy(sig, 0, tmp, tmp.Length - sig.Length, sig.Length);
+ sig = tmp;
+ }
+
+ return Arrays.ConstantTimeAreEqual(sig, hash);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public void Reset()
+ {
+ digest.Reset();
+ }
+ }
}
diff --git a/crypto/src/crypto/signers/HMacDsaKCalculator.cs b/crypto/src/crypto/signers/HMacDsaKCalculator.cs
new file mode 100644
index 000000000..8231197b9
--- /dev/null
+++ b/crypto/src/crypto/signers/HMacDsaKCalculator.cs
@@ -0,0 +1,150 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979.
+ */
+ public class HMacDsaKCalculator
+ : IDsaKCalculator
+ {
+ private readonly HMac hMac;
+ private readonly byte[] K;
+ private readonly byte[] V;
+
+ private BigInteger n;
+
+ /**
+ * Base constructor.
+ *
+ * @param digest digest to build the HMAC on.
+ */
+ public HMacDsaKCalculator(IDigest digest)
+ {
+ this.hMac = new HMac(digest);
+ this.V = new byte[hMac.GetMacSize()];
+ this.K = new byte[hMac.GetMacSize()];
+ }
+
+ public virtual bool IsDeterministic
+ {
+ get { return true; }
+ }
+
+ public virtual void Init(BigInteger n, SecureRandom random)
+ {
+ throw new InvalidOperationException("Operation not supported");
+ }
+
+ public void Init(BigInteger n, BigInteger d, byte[] message)
+ {
+ this.n = n;
+
+ Arrays.Fill(V, (byte)0x01);
+ Arrays.Fill(K, (byte)0);
+
+ byte[] x = new byte[(n.BitLength + 7) / 8];
+ byte[] dVal = BigIntegers.AsUnsignedByteArray(d);
+
+ Array.Copy(dVal, 0, x, x.Length - dVal.Length, dVal.Length);
+
+ byte[] m = new byte[(n.BitLength + 7) / 8];
+
+ BigInteger mInt = BitsToInt(message);
+
+ if (mInt.CompareTo(n) >= 0)
+ {
+ mInt = mInt.Subtract(n);
+ }
+
+ byte[] mVal = BigIntegers.AsUnsignedByteArray(mInt);
+
+ Array.Copy(mVal, 0, m, m.Length - mVal.Length, mVal.Length);
+
+ hMac.Init(new KeyParameter(K));
+
+ hMac.BlockUpdate(V, 0, V.Length);
+ hMac.Update((byte)0x00);
+ hMac.BlockUpdate(x, 0, x.Length);
+ hMac.BlockUpdate(m, 0, m.Length);
+
+ hMac.DoFinal(K, 0);
+
+ hMac.Init(new KeyParameter(K));
+
+ hMac.BlockUpdate(V, 0, V.Length);
+
+ hMac.DoFinal(V, 0);
+
+ hMac.BlockUpdate(V, 0, V.Length);
+ hMac.Update((byte)0x01);
+ hMac.BlockUpdate(x, 0, x.Length);
+ hMac.BlockUpdate(m, 0, m.Length);
+
+ hMac.DoFinal(K, 0);
+
+ hMac.Init(new KeyParameter(K));
+
+ hMac.BlockUpdate(V, 0, V.Length);
+
+ hMac.DoFinal(V, 0);
+ }
+
+ public virtual BigInteger NextK()
+ {
+ byte[] t = new byte[((n.BitLength + 7) / 8)];
+
+ for (;;)
+ {
+ int tOff = 0;
+
+ while (tOff < t.Length)
+ {
+ hMac.BlockUpdate(V, 0, V.Length);
+
+ hMac.DoFinal(V, 0);
+
+ int len = System.Math.Min(t.Length - tOff, V.Length);
+ Array.Copy(V, 0, t, tOff, len);
+ tOff += len;
+ }
+
+ BigInteger k = BitsToInt(t);
+
+ if (k.SignValue > 0 && k.CompareTo(n) < 0)
+ {
+ return k;
+ }
+
+ hMac.BlockUpdate(V, 0, V.Length);
+ hMac.Update((byte)0x00);
+
+ hMac.DoFinal(K, 0);
+
+ hMac.Init(new KeyParameter(K));
+
+ hMac.BlockUpdate(V, 0, V.Length);
+
+ hMac.DoFinal(V, 0);
+ }
+ }
+
+ private BigInteger BitsToInt(byte[] t)
+ {
+ BigInteger v = new BigInteger(1, t);
+
+ if (t.Length * 8 > n.BitLength)
+ {
+ v = v.ShiftRight(t.Length * 8 - n.BitLength);
+ }
+
+ return v;
+ }
+ }
+}
diff --git a/crypto/src/crypto/signers/IDsaKCalculator.cs b/crypto/src/crypto/signers/IDsaKCalculator.cs
new file mode 100644
index 000000000..645186d41
--- /dev/null
+++ b/crypto/src/crypto/signers/IDsaKCalculator.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * Interface define calculators of K values for DSA/ECDSA.
+ */
+ public interface IDsaKCalculator
+ {
+ /**
+ * Return true if this calculator is deterministic, false otherwise.
+ *
+ * @return true if deterministic, otherwise false.
+ */
+ bool IsDeterministic { get; }
+
+ /**
+ * Non-deterministic initialiser.
+ *
+ * @param n the order of the DSA group.
+ * @param random a source of randomness.
+ */
+ void Init(BigInteger n, SecureRandom random);
+
+ /**
+ * Deterministic initialiser.
+ *
+ * @param n the order of the DSA group.
+ * @param d the DSA private value.
+ * @param message the message being signed.
+ */
+ void Init(BigInteger n, BigInteger d, byte[] message);
+
+ /**
+ * Return the next valid value of K.
+ *
+ * @return a K value.
+ */
+ BigInteger NextK();
+ }
+}
diff --git a/crypto/src/crypto/signers/RandomDsaKCalculator.cs b/crypto/src/crypto/signers/RandomDsaKCalculator.cs
new file mode 100644
index 000000000..022cc268d
--- /dev/null
+++ b/crypto/src/crypto/signers/RandomDsaKCalculator.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ public class RandomDsaKCalculator
+ : IDsaKCalculator
+ {
+ private BigInteger q;
+ private SecureRandom random;
+
+ public virtual bool IsDeterministic
+ {
+ get { return false; }
+ }
+
+ public virtual void Init(BigInteger n, SecureRandom random)
+ {
+ this.q = n;
+ this.random = random;
+ }
+
+ public virtual void Init(BigInteger n, BigInteger d, byte[] message)
+ {
+ throw new InvalidOperationException("Operation not supported");
+ }
+
+ public virtual BigInteger NextK()
+ {
+ int qBitLength = q.BitLength;
+
+ BigInteger k;
+ do
+ {
+ k = new BigInteger(qBitLength, random);
+ }
+ while (k.SignValue < 1 || k.CompareTo(q) >= 0);
+
+ return k;
+ }
+ }
+}
diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs
index f57bfc83d..9af4e7145 100644
--- a/crypto/src/crypto/signers/RsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -19,16 +19,16 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Signers
{
public class RsaDigestSigner
- : ISigner
+ : ISigner
{
private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine());
private readonly AlgorithmIdentifier algId;
- private readonly IDigest digest;
- private bool forSigning;
+ private readonly IDigest digest;
+ private bool forSigning;
- private static readonly IDictionary oidMap = Platform.CreateHashtable();
+ private static readonly IDictionary oidMap = Platform.CreateHashtable();
- /// <summary>
+ /// <summary>
/// Load oid table.
/// </summary>
static RsaDigestSigner()
@@ -48,37 +48,37 @@ namespace Org.BouncyCastle.Crypto.Signers
oidMap["MD5"] = PkcsObjectIdentifiers.MD5;
}
- public RsaDigestSigner(
- IDigest digest)
+ public RsaDigestSigner(IDigest digest)
+ : this(digest, (DerObjectIdentifier)oidMap[digest.AlgorithmName])
{
- this.digest = digest;
+ }
- string algName = digest.AlgorithmName;
- if (algName.Equals("NULL"))
- {
- this.algId = null;
- }
- else
- {
- this.algId = new AlgorithmIdentifier(
- (DerObjectIdentifier)oidMap[digest.AlgorithmName], DerNull.Instance);
- }
+ public RsaDigestSigner(IDigest digest, DerObjectIdentifier digestOid)
+ : this(digest, new AlgorithmIdentifier(digestOid, DerNull.Instance))
+ {
}
- public string AlgorithmName
+ public RsaDigestSigner(IDigest digest, AlgorithmIdentifier algId)
+ {
+ this.digest = digest;
+ this.algId = algId;
+ }
+
+ [Obsolete]
+ public string AlgorithmName
{
get { return digest.AlgorithmName + "withRSA"; }
}
- /**
+ /**
* Initialise the signer for signing or verification.
*
* @param forSigning true if for signing, false otherwise
* @param param necessary parameters.
*/
public void Init(
- bool forSigning,
- ICipherParameters parameters)
+ bool forSigning,
+ ICipherParameters parameters)
{
this.forSigning = forSigning;
AsymmetricKeyParameter k;
@@ -95,10 +95,10 @@ namespace Org.BouncyCastle.Crypto.Signers
if (forSigning && !k.IsPrivate)
throw new InvalidKeyException("Signing requires private key.");
- if (!forSigning && k.IsPrivate)
+ if (!forSigning && k.IsPrivate)
throw new InvalidKeyException("Verification requires public key.");
- Reset();
+ Reset();
rsaEngine.Init(forSigning, parameters);
}
@@ -107,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Signers
* update the internal digest with the byte b
*/
public void Update(
- byte input)
+ byte input)
{
digest.Update(input);
}
@@ -116,9 +116,9 @@ namespace Org.BouncyCastle.Crypto.Signers
* update the internal digest with the byte array in
*/
public void BlockUpdate(
- byte[] input,
- int inOff,
- int length)
+ byte[] input,
+ int inOff,
+ int length)
{
digest.BlockUpdate(input, inOff, length);
}
@@ -132,79 +132,69 @@ namespace Org.BouncyCastle.Crypto.Signers
if (!forSigning)
throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation.");
- byte[] hash = new byte[digest.GetDigestSize()];
+ byte[] hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
- byte[] data = DerEncode(hash);
+ byte[] data = DerEncode(hash);
return rsaEngine.ProcessBlock(data, 0, data.Length);
}
- /**
+ /**
* return true if the internal state represents the signature described
* in the passed in array.
*/
public bool VerifySignature(
- byte[] signature)
+ byte[] signature)
{
- if (forSigning)
- throw new InvalidOperationException("RsaDigestSigner not initialised for verification");
-
- byte[] hash = new byte[digest.GetDigestSize()];
- digest.DoFinal(hash, 0);
-
- byte[] sig;
- byte[] expected;
-
- try
- {
- sig = rsaEngine.ProcessBlock(signature, 0, signature.Length);
- expected = DerEncode(hash);
- }
- catch (Exception)
- {
- return false;
- }
-
- if (sig.Length == expected.Length)
- {
- for (int i = 0; i < sig.Length; i++)
- {
- if (sig[i] != expected[i])
- {
- return false;
- }
- }
- }
- else if (sig.Length == expected.Length - 2) // NULL left out
- {
- int sigOffset = sig.Length - hash.Length - 2;
- int expectedOffset = expected.Length - hash.Length - 2;
-
- expected[1] -= 2; // adjust lengths
- expected[3] -= 2;
-
- for (int i = 0; i < hash.Length; i++)
- {
- if (sig[sigOffset + i] != expected[expectedOffset + i]) // check hash
- {
- return false;
- }
- }
-
- for (int i = 0; i < sigOffset; i++)
- {
- if (sig[i] != expected[i]) // check header less NULL
- {
- return false;
- }
- }
- }
- else
- {
- return false;
- }
-
- return true;
+ if (forSigning)
+ throw new InvalidOperationException("RsaDigestSigner not initialised for verification");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ byte[] sig;
+ byte[] expected;
+
+ try
+ {
+ sig = rsaEngine.ProcessBlock(signature, 0, signature.Length);
+ expected = DerEncode(hash);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ if (sig.Length == expected.Length)
+ {
+ return Arrays.ConstantTimeAreEqual(sig, expected);
+ }
+ else if (sig.Length == expected.Length - 2) // NULL left out
+ {
+ int sigOffset = sig.Length - hash.Length - 2;
+ int expectedOffset = expected.Length - hash.Length - 2;
+
+ expected[1] -= 2; // adjust lengths
+ expected[3] -= 2;
+
+ int nonEqual = 0;
+
+ for (int i = 0; i < hash.Length; i++)
+ {
+ nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]);
+ }
+
+ for (int i = 0; i < sigOffset; i++)
+ {
+ nonEqual |= (sig[i] ^ expected[i]); // check header less NULL
+ }
+
+ return nonEqual == 0;
+ }
+ else
+ {
+ return false;
+ }
}
public void Reset()
@@ -212,17 +202,17 @@ namespace Org.BouncyCastle.Crypto.Signers
digest.Reset();
}
- private byte[] DerEncode(byte[] hash)
- {
- if (algId == null)
- {
- // For raw RSA, the DigestInfo must be prepared externally
- return hash;
- }
+ private byte[] DerEncode(byte[] hash)
+ {
+ if (algId == null)
+ {
+ // For raw RSA, the DigestInfo must be prepared externally
+ return hash;
+ }
- DigestInfo dInfo = new DigestInfo(algId, hash);
+ DigestInfo dInfo = new DigestInfo(algId, hash);
- return dInfo.GetDerEncoded();
- }
+ return dInfo.GetDerEncoded();
+ }
}
}
diff --git a/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs
new file mode 100644
index 000000000..2d7af80e8
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsAgreementCredentials
+ : AbstractTlsCredentials, TlsAgreementCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ public abstract byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey);
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs
new file mode 100644
index 000000000..141ee6507
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs
@@ -0,0 +1,15 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class AbstractTlsCipherFactory
+ : TlsCipherFactory
+ {
+ /// <exception cref="IOException"></exception>
+ public virtual TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs
new file mode 100644
index 000000000..9484afa7d
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsClient
+ : AbstractTlsPeer, TlsClient
+ {
+ protected TlsCipherFactory mCipherFactory;
+
+ protected TlsClientContext mContext;
+
+ protected IList mSupportedSignatureAlgorithms;
+ protected int[] mNamedCurves;
+ protected byte[] mClientECPointFormats, mServerECPointFormats;
+
+ protected int mSelectedCipherSuite;
+ protected short mSelectedCompressionMethod;
+
+ public AbstractTlsClient()
+ : this(new DefaultTlsCipherFactory())
+ {
+ }
+
+ public AbstractTlsClient(TlsCipherFactory cipherFactory)
+ {
+ this.mCipherFactory = cipherFactory;
+ }
+
+ public virtual void Init(TlsClientContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual TlsSession GetSessionToResume()
+ {
+ return null;
+ }
+
+ /**
+ * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value
+ * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest
+ * version number supported by the client, and the value of ClientHello.client_version. No
+ * single value will guarantee interoperability with all old servers, but this is a complex
+ * topic beyond the scope of this document."
+ */
+ public virtual ProtocolVersion ClientHelloRecordLayerVersion
+ {
+ get
+ {
+ // "{03,00}"
+ // return ProtocolVersion.SSLv3;
+
+ // "the lowest version number supported by the client"
+ // return getMinimumVersion();
+
+ // "the value of ClientHello.client_version"
+ return ClientVersion;
+ }
+ }
+
+ public virtual ProtocolVersion ClientVersion
+ {
+ get { return ProtocolVersion.TLSv12; }
+ }
+
+ public virtual IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = null;
+
+ ProtocolVersion clientVersion = mContext.ClientVersion;
+
+ /*
+ * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2.
+ * Clients MUST NOT offer it if they are offering prior versions.
+ */
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion))
+ {
+ // TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
+
+ byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+ HashAlgorithm.sha224, HashAlgorithm.sha1 };
+
+ // TODO Sort out ECDSA signatures and add them as the preferred option here
+ byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa };
+
+ this.mSupportedSignatureAlgorithms = Platform.CreateArrayList();
+ for (int i = 0; i < hashAlgorithms.Length; ++i)
+ {
+ for (int j = 0; j < signatureAlgorithms.Length; ++j)
+ {
+ this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i],
+ signatureAlgorithms[j]));
+ }
+ }
+
+ /*
+ * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
+ */
+ this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
+ SignatureAlgorithm.dsa));
+
+ clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
+
+ TlsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, mSupportedSignatureAlgorithms);
+ }
+
+ if (TlsEccUtilities.ContainsEccCipherSuites(GetCipherSuites()))
+ {
+ /*
+ * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
+ * appends these extensions (along with any others), enumerating the curves it supports
+ * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
+ * Curves Extension and the Supported Point Formats Extension.
+ */
+ /*
+ * TODO Could just add all the curves since we support them all, but users may not want
+ * to use unnecessarily large fields. Need configuration options.
+ */
+ this.mNamedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 };
+ this.mClientECPointFormats = new byte[]{ ECPointFormat.uncompressed,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+ clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
+
+ TlsEccUtilities.AddSupportedEllipticCurvesExtension(clientExtensions, mNamedCurves);
+ TlsEccUtilities.AddSupportedPointFormatsExtension(clientExtensions, mClientECPointFormats);
+ }
+
+ return clientExtensions;
+ }
+
+ public virtual ProtocolVersion MinimumVersion
+ {
+ get { return ProtocolVersion.TLSv10; }
+ }
+
+ public virtual void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ if (!MinimumVersion.IsEqualOrEarlierVersionOf(serverVersion))
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+
+ public abstract int[] GetCipherSuites();
+
+ public virtual byte[] GetCompressionMethods()
+ {
+ return new byte[]{ CompressionMethod.cls_null };
+ }
+
+ public virtual void NotifySessionID(byte[] sessionID)
+ {
+ // Currently ignored
+ }
+
+ public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
+ {
+ this.mSelectedCipherSuite = selectedCipherSuite;
+ }
+
+ public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
+ {
+ this.mSelectedCompressionMethod = selectedCompressionMethod;
+ }
+
+ public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ /*
+ * TlsProtocol implementation validates that any server extensions received correspond to
+ * client extensions sent. By default, we don't send any, and this method is not called.
+ */
+ if (serverExtensions != null)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension.
+ */
+ if (serverExtensions.Contains(ExtensionType.signature_algorithms))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ int[] namedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(serverExtensions);
+ if (namedCurves != null)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mServerECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(serverExtensions);
+ if (this.mServerECPointFormats != null && !TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public virtual void ProcessServerSupplementalData(IList serverSupplementalData)
+ {
+ if (serverSupplementalData != null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public abstract TlsKeyExchange GetKeyExchange();
+
+ public abstract TlsAuthentication GetAuthentication();
+
+ public virtual IList GetClientSupplementalData()
+ {
+ return null;
+ }
+
+ public override TlsCompression GetCompression()
+ {
+ switch (mSelectedCompressionMethod)
+ {
+ case CompressionMethod.cls_null:
+ return new TlsNullCompression();
+
+ case CompressionMethod.DEFLATE:
+ return new TlsDeflateCompression();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected compression method was in the list of client-offered compression
+ * methods, so if we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket)
+ {
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs
new file mode 100644
index 000000000..83150d37e
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -0,0 +1,132 @@
+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)
+ {
+ IDigest d = TlsUtilities.CreateHash(HashAlgorithm.sha256);
+ byte[] seed = new byte[d.GetDigestSize()];
+ secureRandom.NextBytes(seed);
+
+ this.mNonceRandom = new DigestRandomGenerator(d);
+ mNonceRandom.AddSeedMaterial(NextCounterValue());
+ mNonceRandom.AddSeedMaterial(Times.NanoTime());
+ mNonceRandom.AddSeedMaterial(seed);
+
+ this.mSecureRandom = secureRandom;
+ this.mSecurityParameters = securityParameters;
+ }
+
+ public virtual IRandomGenerator NonceRandomGenerator
+ {
+ get { return mNonceRandom; }
+ }
+
+ public virtual SecureRandom SecureRandom
+ {
+ get { return mSecureRandom; }
+ }
+
+ public virtual SecurityParameters SecurityParameters
+ {
+ get { return mSecurityParameters; }
+ }
+
+ public abstract bool IsServer { get; }
+
+ public virtual ProtocolVersion ClientVersion
+ {
+ get { return mClientVersion; }
+ }
+
+ internal virtual void SetClientVersion(ProtocolVersion clientVersion)
+ {
+ this.mClientVersion = clientVersion;
+ }
+
+ public virtual ProtocolVersion ServerVersion
+ {
+ get { return mServerVersion; }
+ }
+
+ internal virtual void SetServerVersion(ProtocolVersion serverVersion)
+ {
+ this.mServerVersion = serverVersion;
+ }
+
+ public virtual TlsSession ResumableSession
+ {
+ get { return mSession; }
+ }
+
+ internal virtual void SetResumableSession(TlsSession session)
+ {
+ this.mSession = session;
+ }
+
+ public virtual object UserObject
+ {
+ get { return mUserObject; }
+ set { this.mUserObject = value; }
+ }
+
+ public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length)
+ {
+ if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length))
+ throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value");
+
+ SecurityParameters sp = SecurityParameters;
+ byte[] cr = sp.ClientRandom, sr = sp.ServerRandom;
+
+ int seedLength = cr.Length + sr.Length;
+ if (context_value != null)
+ {
+ seedLength += (2 + context_value.Length);
+ }
+
+ byte[] seed = new byte[seedLength];
+ int seedPos = 0;
+
+ Array.Copy(cr, 0, seed, seedPos, cr.Length);
+ seedPos += cr.Length;
+ Array.Copy(sr, 0, seed, seedPos, sr.Length);
+ seedPos += sr.Length;
+ if (context_value != null)
+ {
+ TlsUtilities.WriteUint16(context_value.Length, seed, seedPos);
+ seedPos += 2;
+ Array.Copy(context_value, 0, seed, seedPos, context_value.Length);
+ seedPos += context_value.Length;
+ }
+
+ if (seedPos != seedLength)
+ throw new InvalidOperationException("error in calculation of seed for export");
+
+ return TlsUtilities.PRF(this, sp.MasterSecret, asciiLabel, seed, length);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsCredentials.cs b/crypto/src/crypto/tls/AbstractTlsCredentials.cs
new file mode 100644
index 000000000..6411b811c
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsCredentials.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsCredentials
+ : TlsCredentials
+ {
+ public abstract Certificate Certificate { get; }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs
new file mode 100644
index 000000000..05b129c60
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsEncryptionCredentials
+ : AbstractTlsCredentials, TlsEncryptionCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ public abstract byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret);
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
new file mode 100644
index 000000000..155ac94d8
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsKeyExchange
+ : TlsKeyExchange
+ {
+ protected readonly int mKeyExchange;
+ protected IList mSupportedSignatureAlgorithms;
+
+ protected TlsContext context;
+
+ protected AbstractTlsKeyExchange(int keyExchange, IList supportedSignatureAlgorithms)
+ {
+ this.mKeyExchange = keyExchange;
+ this.mSupportedSignatureAlgorithms = supportedSignatureAlgorithms;
+ }
+
+ public virtual void Init(TlsContext context)
+ {
+ this.context = context;
+
+ ProtocolVersion clientVersion = context.ClientVersion;
+
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion))
+ {
+ /*
+ * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension,
+ * the server MUST do the following:
+ *
+ * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK,
+ * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}.
+ *
+ * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if
+ * the client had sent the value {sha1,dsa}.
+ *
+ * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA),
+ * behave as if the client had sent value {sha1,ecdsa}.
+ */
+ if (this.mSupportedSignatureAlgorithms == null)
+ {
+ switch (mKeyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ {
+ this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultDssSignatureAlgorithms();
+ break;
+ }
+
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ {
+ this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ break;
+ }
+
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.RSA:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ {
+ this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+ break;
+ }
+
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.SRP:
+ break;
+
+ default:
+ throw new InvalidOperationException("unsupported key exchange algorithm");
+ }
+ }
+
+ }
+ else if (this.mSupportedSignatureAlgorithms != null)
+ {
+ throw new InvalidOperationException("supported_signature_algorithms not allowed for " + clientVersion);
+ }
+ }
+
+ public abstract void SkipServerCredentials();
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ if (mSupportedSignatureAlgorithms == null)
+ {
+ /*
+ * TODO RFC 2264 7.4.2. Unless otherwise specified, the signing algorithm for the
+ * certificate must be the same as the algorithm for the certificate key.
+ */
+ }
+ else
+ {
+ /*
+ * TODO RFC 5264 7.4.2. If the client provided a "signature_algorithms" extension, then
+ * all certificates provided by the server MUST be signed by a hash/signature algorithm
+ * pair that appears in that extension.
+ */
+ }
+ }
+
+ public virtual void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ ProcessServerCertificate(serverCredentials.Certificate);
+ }
+
+ public virtual bool RequiresServerKeyExchange
+ {
+ get { return false; }
+ }
+
+ public virtual byte[] GenerateServerKeyExchange()
+ {
+ if (RequiresServerKeyExchange)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return null;
+ }
+
+ public virtual void SkipServerKeyExchange()
+ {
+ if (RequiresServerKeyExchange)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ProcessServerKeyExchange(Stream input)
+ {
+ if (!RequiresServerKeyExchange)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public abstract void ValidateCertificateRequest(CertificateRequest certificateRequest);
+
+ public virtual void SkipClientCredentials()
+ {
+ }
+
+ public abstract void ProcessClientCredentials(TlsCredentials clientCredentials);
+
+ public virtual void ProcessClientCertificate(Certificate clientCertificate)
+ {
+ }
+
+ public abstract void GenerateClientKeyExchange(Stream output);
+
+ public virtual void ProcessClientKeyExchange(Stream input)
+ {
+ // Key exchange implementation MUST support client key exchange
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public abstract byte[] GeneratePremasterSecret();
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs
new file mode 100644
index 000000000..81a53386c
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs
@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsPeer
+ : TlsPeer
+ {
+ public virtual bool ShouldUseGmtUnixTime()
+ {
+ /*
+ * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
+ * TLS implementors MUST by default set the entire value the ClientHello.Random and
+ * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
+ * sequence.
+ */
+ return false;
+ }
+
+ public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead
+ * of continuing; see Section 4.1/4.3 for discussion.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public abstract TlsCompression GetCompression();
+
+ public abstract TlsCipher GetCipher();
+
+ public virtual void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ }
+
+ public virtual void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+ {
+ }
+
+ public virtual void NotifyHandshakeComplete()
+ {
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs
new file mode 100644
index 000000000..c2c6fd57c
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -0,0 +1,321 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsServer
+ : AbstractTlsPeer, TlsServer
+ {
+ protected TlsCipherFactory mCipherFactory;
+
+ protected TlsServerContext mContext;
+
+ protected ProtocolVersion mClientVersion;
+ protected int[] mOfferedCipherSuites;
+ protected byte[] mOfferedCompressionMethods;
+ protected IDictionary mClientExtensions;
+
+ protected bool mEncryptThenMacOffered;
+ protected short mMaxFragmentLengthOffered;
+ protected bool mTruncatedHMacOffered;
+ protected IList mSupportedSignatureAlgorithms;
+ protected bool mEccCipherSuitesOffered;
+ protected int[] mNamedCurves;
+ protected byte[] mClientECPointFormats, mServerECPointFormats;
+
+ protected ProtocolVersion mServerVersion;
+ protected int mSelectedCipherSuite;
+ protected byte mSelectedCompressionMethod;
+ protected IDictionary mServerExtensions;
+
+ public AbstractTlsServer()
+ : this(new DefaultTlsCipherFactory())
+ {
+ }
+
+ public AbstractTlsServer(TlsCipherFactory cipherFactory)
+ {
+ this.mCipherFactory = cipherFactory;
+ }
+
+ protected virtual bool AllowEncryptThenMac
+ {
+ get { return true; }
+ }
+
+ protected virtual bool AllowTruncatedHMac
+ {
+ get { return false; }
+ }
+
+ protected virtual IDictionary CheckServerExtensions()
+ {
+ return this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mServerExtensions);
+ }
+
+ protected abstract int[] GetCipherSuites();
+
+ protected byte[] GetCompressionMethods()
+ {
+ return new byte[] { CompressionMethod.cls_null };
+ }
+
+ protected virtual ProtocolVersion MaximumVersion
+ {
+ get { return ProtocolVersion.TLSv11; }
+ }
+
+ protected virtual ProtocolVersion MinimumVersion
+ {
+ get { return ProtocolVersion.TLSv10; }
+ }
+
+ protected virtual bool SupportsClientEccCapabilities(int[] namedCurves, byte[] ecPointFormats)
+ {
+ // NOTE: BC supports all the current set of point formats so we don't check them here
+
+ if (namedCurves == null)
+ {
+ /*
+ * RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these
+ * extensions. In this case, the server is free to choose any one of the elliptic curves
+ * or point formats [...].
+ */
+ return TlsEccUtilities.HasAnySupportedNamedCurves();
+ }
+
+ for (int i = 0; i < namedCurves.Length; ++i)
+ {
+ int namedCurve = namedCurves[i];
+ if (NamedCurve.IsValid(namedCurve)
+ && (!NamedCurve.RefersToASpecificNamedCurve(namedCurve) || TlsEccUtilities.IsSupportedNamedCurve(namedCurve)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public virtual void Init(TlsServerContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual void NotifyClientVersion(ProtocolVersion clientVersion)
+ {
+ this.mClientVersion = clientVersion;
+ }
+
+ public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites)
+ {
+ this.mOfferedCipherSuites = offeredCipherSuites;
+ this.mEccCipherSuitesOffered = TlsEccUtilities.ContainsEccCipherSuites(this.mOfferedCipherSuites);
+ }
+
+ public virtual void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods)
+ {
+ this.mOfferedCompressionMethods = offeredCompressionMethods;
+ }
+
+ public virtual void ProcessClientExtensions(IDictionary clientExtensions)
+ {
+ this.mClientExtensions = clientExtensions;
+
+ if (clientExtensions != null)
+ {
+ this.mEncryptThenMacOffered = TlsExtensionsUtilities.HasEncryptThenMacExtension(clientExtensions);
+ this.mMaxFragmentLengthOffered = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions);
+ this.mTruncatedHMacOffered = TlsExtensionsUtilities.HasTruncatedHMacExtension(clientExtensions);
+
+ this.mSupportedSignatureAlgorithms = TlsUtilities.GetSignatureAlgorithmsExtension(clientExtensions);
+ if (this.mSupportedSignatureAlgorithms != null)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior
+ * to 1.2. Clients MUST NOT offer it if they are offering prior versions.
+ */
+ if (!TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mClientVersion))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.mNamedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(clientExtensions);
+ this.mClientECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(clientExtensions);
+ }
+
+ /*
+ * RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it
+ * does not propose any ECC cipher suites.
+ */
+ if (!this.mEccCipherSuitesOffered && (this.mNamedCurves != null || this.mClientECPointFormats != null))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ public virtual ProtocolVersion GetServerVersion()
+ {
+ if (MinimumVersion.IsEqualOrEarlierVersionOf(mClientVersion))
+ {
+ ProtocolVersion maximumVersion = MaximumVersion;
+ if (mClientVersion.IsEqualOrEarlierVersionOf(maximumVersion))
+ {
+ return mServerVersion = mClientVersion;
+ }
+ if (mClientVersion.IsLaterVersionOf(maximumVersion))
+ {
+ return mServerVersion = maximumVersion;
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+
+ public virtual int GetSelectedCipherSuite()
+ {
+ /*
+ * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate
+ * cipher suites against the "signature_algorithms" extension before selecting them. This is
+ * somewhat inelegant but is a compromise designed to minimize changes to the original
+ * cipher suite design.
+ */
+
+ /*
+ * RFC 4429 5.1. A server that receives a ClientHello containing one or both of these
+ * extensions MUST use the client's enumerated capabilities to guide its selection of an
+ * appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only
+ * if the server can successfully complete the handshake while using the curves and point
+ * formats supported by the client [...].
+ */
+ bool eccCipherSuitesEnabled = SupportsClientEccCapabilities(this.mNamedCurves, this.mClientECPointFormats);
+
+ int[] cipherSuites = GetCipherSuites();
+ for (int i = 0; i < cipherSuites.Length; ++i)
+ {
+ int cipherSuite = cipherSuites[i];
+
+ if (Arrays.Contains(this.mOfferedCipherSuites, cipherSuite)
+ && (eccCipherSuitesEnabled || !TlsEccUtilities.IsEccCipherSuite(cipherSuite))
+ && TlsUtilities.IsValidCipherSuiteForVersion(cipherSuite, mServerVersion))
+ {
+ return this.mSelectedCipherSuite = cipherSuite;
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ public virtual byte GetSelectedCompressionMethod()
+ {
+ byte[] compressionMethods = GetCompressionMethods();
+ for (int i = 0; i < compressionMethods.Length; ++i)
+ {
+ if (Arrays.Contains(mOfferedCompressionMethods, compressionMethods[i]))
+ {
+ return this.mSelectedCompressionMethod = compressionMethods[i];
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ // IDictionary is (Int32 -> byte[])
+ public virtual IDictionary GetServerExtensions()
+ {
+ if (this.mEncryptThenMacOffered && AllowEncryptThenMac)
+ {
+ /*
+ * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
+ * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
+ * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
+ * client.
+ */
+ if (TlsUtilities.IsBlockCipherSuite(this.mSelectedCipherSuite))
+ {
+ TlsExtensionsUtilities.AddEncryptThenMacExtension(CheckServerExtensions());
+ }
+ }
+
+ if (this.mMaxFragmentLengthOffered >= 0
+ && TlsUtilities.IsValidUint8(mMaxFragmentLengthOffered)
+ && MaxFragmentLength.IsValid((byte)mMaxFragmentLengthOffered))
+ {
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)mMaxFragmentLengthOffered);
+ }
+
+ if (this.mTruncatedHMacOffered && AllowTruncatedHMac)
+ {
+ TlsExtensionsUtilities.AddTruncatedHMacExtension(CheckServerExtensions());
+ }
+
+ if (this.mClientECPointFormats != null && TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite))
+ {
+ /*
+ * RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello
+ * message including a Supported Point Formats Extension appends this extension (along
+ * with others) to its ServerHello message, enumerating the point formats it can parse.
+ */
+ this.mServerECPointFormats = new byte[]{ ECPointFormat.uncompressed,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+ TlsEccUtilities.AddSupportedPointFormatsExtension(CheckServerExtensions(), mServerECPointFormats);
+ }
+
+ return mServerExtensions;
+ }
+
+ public virtual IList GetServerSupplementalData()
+ {
+ return null;
+ }
+
+ public abstract TlsCredentials GetCredentials();
+
+ public virtual CertificateStatus GetCertificateStatus()
+ {
+ return null;
+ }
+
+ public abstract TlsKeyExchange GetKeyExchange();
+
+ public virtual CertificateRequest GetCertificateRequest()
+ {
+ return null;
+ }
+
+ public virtual void ProcessClientSupplementalData(IList clientSupplementalData)
+ {
+ if (clientSupplementalData != null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public override TlsCompression GetCompression()
+ {
+ switch (mSelectedCompressionMethod)
+ {
+ case CompressionMethod.cls_null:
+ return new TlsNullCompression();
+
+ default:
+ /*
+ * Note: internal error here; we selected the compression method, so if we now can't
+ * produce an implementation, we shouldn't have chosen it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual NewSessionTicket GetNewSessionTicket()
+ {
+ /*
+ * RFC 5077 3.3. If the server determines that it does not want to include a ticket after it
+ * has included the SessionTicket extension in the ServerHello, then it sends a zero-length
+ * ticket in the NewSessionTicket handshake message.
+ */
+ return new NewSessionTicket(0L, TlsUtilities.EmptyBytes);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsSigner.cs b/crypto/src/crypto/tls/AbstractTlsSigner.cs
new file mode 100644
index 000000000..1f4aabf74
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsSigner.cs
@@ -0,0 +1,50 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsSigner
+ : TlsSigner
+ {
+ protected TlsContext mContext;
+
+ public virtual void Init(TlsContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ {
+ return GenerateRawSignature(null, privateKey, md5AndSha1);
+ }
+
+ public abstract byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+ AsymmetricKeyParameter privateKey, byte[] hash);
+
+ public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ {
+ return VerifyRawSignature(null, sigBytes, publicKey, md5AndSha1);
+ }
+
+ public abstract bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+ AsymmetricKeyParameter publicKey, byte[] hash);
+
+ public virtual ISigner CreateSigner(AsymmetricKeyParameter privateKey)
+ {
+ return CreateSigner(null, privateKey);
+ }
+
+ public abstract ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey);
+
+ public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+ {
+ return CreateVerifyer(null, publicKey);
+ }
+
+ public abstract ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey);
+
+ public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs
new file mode 100644
index 000000000..886c46c6e
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs
@@ -0,0 +1,20 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsSignerCredentials
+ : AbstractTlsCredentials, TlsSignerCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ public abstract byte[] GenerateCertificateSignature(byte[] hash);
+
+ public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+ {
+ get
+ {
+ throw new InvalidOperationException("TlsSignerCredentials implementation does not support (D)TLS 1.2+");
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AlertDescription.cs b/crypto/src/crypto/tls/AlertDescription.cs
index e09da6cab..5b6e88bf7 100644
--- a/crypto/src/crypto/tls/AlertDescription.cs
+++ b/crypto/src/crypto/tls/AlertDescription.cs
@@ -213,5 +213,79 @@ namespace Org.BouncyCastle.Crypto.Tls
* "unknown_psk_identity" alert message.
*/
public const byte unknown_psk_identity = 115;
+
+ public static string GetName(byte alertDescription)
+ {
+ switch (alertDescription)
+ {
+ case close_notify:
+ return "close_notify";
+ case unexpected_message:
+ return "unexpected_message";
+ case bad_record_mac:
+ return "bad_record_mac";
+ case decryption_failed:
+ return "decryption_failed";
+ case record_overflow:
+ return "record_overflow";
+ case decompression_failure:
+ return "decompression_failure";
+ case handshake_failure:
+ return "handshake_failure";
+ case no_certificate:
+ return "no_certificate";
+ case bad_certificate:
+ return "bad_certificate";
+ case unsupported_certificate:
+ return "unsupported_certificate";
+ case certificate_revoked:
+ return "certificate_revoked";
+ case certificate_expired:
+ return "certificate_expired";
+ case certificate_unknown:
+ return "certificate_unknown";
+ case illegal_parameter:
+ return "illegal_parameter";
+ case unknown_ca:
+ return "unknown_ca";
+ case access_denied:
+ return "access_denied";
+ case decode_error:
+ return "decode_error";
+ case decrypt_error:
+ return "decrypt_error";
+ case export_restriction:
+ return "export_restriction";
+ case protocol_version:
+ return "protocol_version";
+ case insufficient_security:
+ return "insufficient_security";
+ case internal_error:
+ return "internal_error";
+ case user_canceled:
+ return "user_canceled";
+ case no_renegotiation:
+ return "no_renegotiation";
+ case unsupported_extension:
+ return "unsupported_extension";
+ case certificate_unobtainable:
+ return "certificate_unobtainable";
+ case unrecognized_name:
+ return "unrecognized_name";
+ case bad_certificate_status_response:
+ return "bad_certificate_status_response";
+ case bad_certificate_hash_value:
+ return "bad_certificate_hash_value";
+ case unknown_psk_identity:
+ return "unknown_psk_identity";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(byte alertDescription)
+ {
+ return GetName(alertDescription) + "(" + alertDescription + ")";
+ }
}
}
diff --git a/crypto/src/crypto/tls/AlertLevel.cs b/crypto/src/crypto/tls/AlertLevel.cs
index d77251dfb..9461a0b58 100644
--- a/crypto/src/crypto/tls/AlertLevel.cs
+++ b/crypto/src/crypto/tls/AlertLevel.cs
@@ -7,5 +7,23 @@ namespace Org.BouncyCastle.Crypto.Tls
{
public const byte warning = 1;
public const byte fatal = 2;
+
+ public static string GetName(byte alertDescription)
+ {
+ switch (alertDescription)
+ {
+ case warning:
+ return "warning";
+ case fatal:
+ return "fatal";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(byte alertDescription)
+ {
+ return GetName(alertDescription) + "(" + alertDescription + ")";
+ }
}
}
diff --git a/crypto/src/crypto/tls/AlwaysValidVerifyer.cs b/crypto/src/crypto/tls/AlwaysValidVerifyer.cs
deleted file mode 100644
index e26c6fc3f..000000000
--- a/crypto/src/crypto/tls/AlwaysValidVerifyer.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-using Org.BouncyCastle.Asn1.X509;
-
-namespace Org.BouncyCastle.Crypto.Tls
-{
- /// <remarks>
- /// A certificate verifyer, that will always return true.
- /// <pre>
- /// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING.
- /// </pre>
- /// </remarks>
- [Obsolete("Perform certificate verification in TlsAuthentication implementation")]
- public class AlwaysValidVerifyer
- : ICertificateVerifyer
- {
- /// <summary>Return true.</summary>
- public bool IsValid(
- X509CertificateStructure[] certs)
- {
- return true;
- }
- }
-}
diff --git a/crypto/src/crypto/tls/Certificate.cs b/crypto/src/crypto/tls/Certificate.cs
index 12bfa9214..c59616c95 100644
--- a/crypto/src/crypto/tls/Certificate.cs
+++ b/crypto/src/crypto/tls/Certificate.cs
@@ -38,13 +38,6 @@ namespace Org.BouncyCastle.Crypto.Tls
this.mCertificateList = certificateList;
}
- /// <returns>An array which contains the certs, this chain contains.</returns>
- [Obsolete("Use 'GetCertificateList' instead")]
- public virtual X509CertificateStructure[] GetCerts()
- {
- return GetCertificateList();
- }
-
/**
* @return an array of {@link org.bouncycastle.asn1.x509.Certificate} representing a certificate
* chain.
diff --git a/crypto/src/crypto/tls/CertificateRequest.cs b/crypto/src/crypto/tls/CertificateRequest.cs
index 100398ffb..f3dcb3bbd 100644
--- a/crypto/src/crypto/tls/CertificateRequest.cs
+++ b/crypto/src/crypto/tls/CertificateRequest.cs
@@ -123,8 +123,7 @@ namespace Org.BouncyCastle.Crypto.Tls
* @return a {@link CertificateRequest} object.
* @throws IOException
*/
- public static CertificateRequest Parse(//TlsContext context,
- Stream input)
+ public static CertificateRequest Parse(TlsContext context, Stream input)
{
int numTypes = TlsUtilities.ReadUint8(input);
byte[] certificateTypes = new byte[numTypes];
@@ -133,13 +132,12 @@ namespace Org.BouncyCastle.Crypto.Tls
certificateTypes[i] = TlsUtilities.ReadUint8(input);
}
- // TODO Add TLS 1.2 support here
IList supportedSignatureAlgorithms = null;
- //if (TlsUtilities.IsTLSv12(context))
- //{
- // // TODO Check whether SignatureAlgorithm.anonymous is allowed here
- // supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input);
- //}
+ if (TlsUtilities.IsTlsV12(context))
+ {
+ // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+ supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input);
+ }
IList certificateAuthorities = Platform.CreateArrayList();
byte[] certAuthData = TlsUtilities.ReadOpaque16(input);
diff --git a/crypto/src/crypto/tls/Chacha20Poly1305.cs b/crypto/src/crypto/tls/Chacha20Poly1305.cs
new file mode 100644
index 000000000..e4e4c7ee2
--- /dev/null
+++ b/crypto/src/crypto/tls/Chacha20Poly1305.cs
@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class Chacha20Poly1305
+ : TlsCipher
+ {
+ protected readonly TlsContext context;
+
+ protected readonly ChaChaEngine encryptCipher;
+ protected readonly ChaChaEngine decryptCipher;
+
+ /// <exception cref="IOException"></exception>
+ public Chacha20Poly1305(TlsContext context)
+ {
+ if (!TlsUtilities.IsTlsV12(context))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.context = context;
+
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, 64);
+
+ KeyParameter client_write_key = new KeyParameter(key_block, 0, 32);
+ KeyParameter server_write_key = new KeyParameter(key_block, 32, 32);
+
+ this.encryptCipher = new ChaChaEngine(20);
+ this.decryptCipher = new ChaChaEngine(20);
+
+ KeyParameter encryptKey, decryptKey;
+ if (context.IsServer)
+ {
+ encryptKey = server_write_key;
+ decryptKey = client_write_key;
+ }
+ else
+ {
+ encryptKey = client_write_key;
+ decryptKey = server_write_key;
+ }
+
+ byte[] dummyNonce = new byte[8];
+
+ this.encryptCipher.Init(true, new ParametersWithIV(encryptKey, dummyNonce));
+ this.decryptCipher.Init(false, new ParametersWithIV(decryptKey, dummyNonce));
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - 16;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
+ {
+ int ciphertextLength = len + 16;
+
+ KeyParameter macKey = InitRecordMac(encryptCipher, true, seqNo);
+
+ byte[] output = new byte[ciphertextLength];
+ encryptCipher.ProcessBytes(plaintext, offset, len, output, 0);
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, len);
+ byte[] mac = CalculateRecordMac(macKey, additionalData, output, 0, len);
+ Array.Copy(mac, 0, output, len, mac.Length);
+
+ return output;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
+ {
+ if (GetPlaintextLimit(len) < 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int plaintextLength = len - 16;
+
+ byte[] receivedMAC = Arrays.CopyOfRange(ciphertext, offset + plaintextLength, offset + len);
+
+ KeyParameter macKey = InitRecordMac(decryptCipher, false, seqNo);
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
+ byte[] calculatedMAC = CalculateRecordMac(macKey, additionalData, ciphertext, offset, plaintextLength);
+
+ if (!Arrays.ConstantTimeAreEqual(calculatedMAC, receivedMAC))
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ byte[] output = new byte[plaintextLength];
+ decryptCipher.ProcessBytes(ciphertext, offset, plaintextLength, output, 0);
+
+ return output;
+ }
+
+ protected virtual KeyParameter InitRecordMac(ChaChaEngine cipher, bool forEncryption, long seqNo)
+ {
+ byte[] nonce = new byte[8];
+ TlsUtilities.WriteUint64(seqNo, nonce, 0);
+
+ cipher.Init(forEncryption, new ParametersWithIV(null, nonce));
+
+ byte[] firstBlock = new byte[64];
+ cipher.ProcessBytes(firstBlock, 0, firstBlock.Length, firstBlock, 0);
+
+ // NOTE: The BC implementation puts 'r' after 'k'
+ Array.Copy(firstBlock, 0, firstBlock, 32, 16);
+ KeyParameter macKey = new KeyParameter(firstBlock, 16, 32);
+ Poly1305KeyGenerator.Clamp(macKey.GetKey());
+ return macKey;
+ }
+
+ protected virtual byte[] CalculateRecordMac(KeyParameter macKey, byte[] additionalData, byte[] buf, int off, int len)
+ {
+ IMac mac = new Poly1305();
+ mac.Init(macKey);
+
+ UpdateRecordMac(mac, additionalData, 0, additionalData.Length);
+ UpdateRecordMac(mac, buf, off, len);
+ return MacUtilities.DoFinal(mac);
+ }
+
+ protected virtual void UpdateRecordMac(IMac mac, byte[] buf, int off, int len)
+ {
+ mac.BlockUpdate(buf, off, len);
+
+ byte[] longLen = Pack.UInt64_To_LE((ulong)len);
+ mac.BlockUpdate(longLen, 0, longLen.Length);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len)
+ {
+ /*
+ * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
+ * TLSCompressed.length
+ */
+ byte[] additional_data = new byte[13];
+ TlsUtilities.WriteUint64(seqNo, additional_data, 0);
+ TlsUtilities.WriteUint8(type, additional_data, 8);
+ TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9);
+ TlsUtilities.WriteUint16(len, additional_data, 11);
+
+ return additional_data;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/CombinedHash.cs b/crypto/src/crypto/tls/CombinedHash.cs
index 59ad87a7b..74a52d598 100644
--- a/crypto/src/crypto/tls/CombinedHash.cs
+++ b/crypto/src/crypto/tls/CombinedHash.cs
@@ -1,82 +1,133 @@
using System;
-using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <remarks>A combined hash, which implements md5(m) || sha1(m).</remarks>
- internal class CombinedHash
- : IDigest
- {
- private readonly MD5Digest md5;
- private readonly Sha1Digest sha1;
-
- internal CombinedHash()
- {
- this.md5 = new MD5Digest();
- this.sha1 = new Sha1Digest();
- }
-
- internal CombinedHash(CombinedHash t)
- {
- this.md5 = new MD5Digest(t.md5);
- this.sha1 = new Sha1Digest(t.sha1);
- }
-
- /// <seealso cref="IDigest.AlgorithmName"/>
- public string AlgorithmName
- {
- get
- {
- return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0";
- }
- }
-
- /// <seealso cref="IDigest.GetByteLength"/>
- public int GetByteLength()
- {
- return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength());
- }
-
- /// <seealso cref="IDigest.GetDigestSize"/>
- public int GetDigestSize()
- {
- return md5.GetDigestSize() + sha1.GetDigestSize();
- }
-
- /// <seealso cref="IDigest.Update"/>
- public void Update(
- byte input)
- {
- md5.Update(input);
- sha1.Update(input);
- }
-
- /// <seealso cref="IDigest.BlockUpdate"/>
- public void BlockUpdate(
- byte[] input,
- int inOff,
- int len)
- {
- md5.BlockUpdate(input, inOff, len);
- sha1.BlockUpdate(input, inOff, len);
- }
-
- /// <seealso cref="IDigest.DoFinal"/>
- public int DoFinal(
- byte[] output,
- int outOff)
- {
- int i1 = md5.DoFinal(output, outOff);
- int i2 = sha1.DoFinal(output, outOff + i1);
- return i1 + i2;
- }
-
- /// <seealso cref="IDigest.Reset"/>
- public void Reset()
- {
- md5.Reset();
- sha1.Reset();
- }
- }
+ /**
+ * A combined hash, which implements md5(m) || sha1(m).
+ */
+ internal class CombinedHash
+ : TlsHandshakeHash
+ {
+ protected TlsContext mContext;
+ protected IDigest mMd5;
+ protected IDigest mSha1;
+
+ internal CombinedHash()
+ {
+ this.mMd5 = TlsUtilities.CreateHash(HashAlgorithm.md5);
+ this.mSha1 = TlsUtilities.CreateHash(HashAlgorithm.sha1);
+ }
+
+ internal CombinedHash(CombinedHash t)
+ {
+ this.mContext = t.mContext;
+ this.mMd5 = TlsUtilities.CloneHash(HashAlgorithm.md5, t.mMd5);
+ this.mSha1 = TlsUtilities.CloneHash(HashAlgorithm.sha1, t.mSha1);
+ }
+
+ public virtual void Init(TlsContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual TlsHandshakeHash NotifyPrfDetermined()
+ {
+ return this;
+ }
+
+ public virtual void TrackHashAlgorithm(byte hashAlgorithm)
+ {
+ throw new InvalidOperationException("CombinedHash only supports calculating the legacy PRF for handshake hash");
+ }
+
+ public virtual void SealHashAlgorithms()
+ {
+ }
+
+ public virtual TlsHandshakeHash StopTracking()
+ {
+ return new CombinedHash(this);
+ }
+
+ public virtual IDigest ForkPrfHash()
+ {
+ return new CombinedHash(this);
+ }
+
+ public virtual byte[] GetFinalHash(byte hashAlgorithm)
+ {
+ throw new InvalidOperationException("CombinedHash doesn't support multiple hashes");
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return mMd5.AlgorithmName + " and " + mSha1.AlgorithmName; }
+ }
+
+ public virtual int GetByteLength()
+ {
+ return System.Math.Max(mMd5.GetByteLength(), mSha1.GetByteLength());
+ }
+
+ public virtual int GetDigestSize()
+ {
+ return mMd5.GetDigestSize() + mSha1.GetDigestSize();
+ }
+
+ public virtual void Update(byte input)
+ {
+ mMd5.Update(input);
+ mSha1.Update(input);
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#update(byte[], int, int)
+ */
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ mMd5.BlockUpdate(input, inOff, len);
+ mSha1.BlockUpdate(input, inOff, len);
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#doFinal(byte[], int)
+ */
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ if (mContext != null && TlsUtilities.IsSsl(mContext))
+ {
+ Ssl3Complete(mMd5, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 48);
+ Ssl3Complete(mSha1, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 40);
+ }
+
+ int i1 = mMd5.DoFinal(output, outOff);
+ int i2 = mSha1.DoFinal(output, outOff + i1);
+ return i1 + i2;
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#reset()
+ */
+ public virtual void Reset()
+ {
+ mMd5.Reset();
+ mSha1.Reset();
+ }
+
+ protected virtual void Ssl3Complete(IDigest d, byte[] ipad, byte[] opad, int padLength)
+ {
+ byte[] master_secret = mContext.SecurityParameters.masterSecret;
+
+ d.BlockUpdate(master_secret, 0, master_secret.Length);
+ d.BlockUpdate(ipad, 0, padLength);
+
+ byte[] tmp = DigestUtilities.DoFinal(d);
+
+ d.BlockUpdate(master_secret, 0, master_secret.Length);
+ d.BlockUpdate(opad, 0, padLength);
+ d.BlockUpdate(tmp, 0, tmp.Length);
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/CompressionMethod.cs b/crypto/src/crypto/tls/CompressionMethod.cs
index e4ee9666f..89c1f5ff4 100644
--- a/crypto/src/crypto/tls/CompressionMethod.cs
+++ b/crypto/src/crypto/tls/CompressionMethod.cs
@@ -7,7 +7,7 @@ namespace Org.BouncyCastle.Crypto.Tls
/// </summary>
public abstract class CompressionMethod
{
- public const byte NULL = 0;
+ public const byte cls_null = 0;
/*
* RFC 3749 2
diff --git a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
index 2bd2f40bf..5147a1990 100644
--- a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
+++ b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Parameters;
@@ -8,69 +9,61 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
public class DefaultTlsAgreementCredentials
- : TlsAgreementCredentials
+ : AbstractTlsAgreementCredentials
{
- protected Certificate clientCert;
- protected AsymmetricKeyParameter clientPrivateKey;
+ protected readonly Certificate mCertificate;
+ protected readonly AsymmetricKeyParameter mPrivateKey;
- protected IBasicAgreement basicAgreement;
- protected bool truncateAgreement;
+ protected readonly IBasicAgreement mBasicAgreement;
+ protected readonly bool mTruncateAgreement;
- public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+ public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey)
{
- if (clientCertificate == null)
- {
- throw new ArgumentNullException("clientCertificate");
- }
- if (clientCertificate.Length == 0)
- {
- throw new ArgumentException("cannot be empty", "clientCertificate");
- }
- if (clientPrivateKey == null)
- {
- throw new ArgumentNullException("clientPrivateKey");
- }
- if (!clientPrivateKey.IsPrivate)
- {
- throw new ArgumentException("must be private", "clientPrivateKey");
- }
+ if (certificate == null)
+ throw new ArgumentNullException("certificate");
+ if (certificate.IsEmpty)
+ throw new ArgumentException("cannot be empty", "certificate");
+ if (privateKey == null)
+ throw new ArgumentNullException("privateKey");
+ if (!privateKey.IsPrivate)
+ throw new ArgumentException("must be private", "privateKey");
- if (clientPrivateKey is DHPrivateKeyParameters)
+ if (privateKey is DHPrivateKeyParameters)
{
- basicAgreement = new DHBasicAgreement();
- truncateAgreement = true;
+ mBasicAgreement = new DHBasicAgreement();
+ mTruncateAgreement = true;
}
- else if (clientPrivateKey is ECPrivateKeyParameters)
+ else if (privateKey is ECPrivateKeyParameters)
{
- basicAgreement = new ECDHBasicAgreement();
- truncateAgreement = false;
+ mBasicAgreement = new ECDHBasicAgreement();
+ mTruncateAgreement = false;
}
else
{
- throw new ArgumentException("type not supported: "
- + clientPrivateKey.GetType().FullName, "clientPrivateKey");
+ throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey");
}
- this.clientCert = clientCertificate;
- this.clientPrivateKey = clientPrivateKey;
+ this.mCertificate = certificate;
+ this.mPrivateKey = privateKey;
}
- public virtual Certificate Certificate
+ public override Certificate Certificate
{
- get { return clientCert; }
+ get { return mCertificate; }
}
- public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey)
+ /// <exception cref="IOException"></exception>
+ public override byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey)
{
- basicAgreement.Init(clientPrivateKey);
- BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey);
+ mBasicAgreement.Init(mPrivateKey);
+ BigInteger agreementValue = mBasicAgreement.CalculateAgreement(peerPublicKey);
- if (truncateAgreement)
+ if (mTruncateAgreement)
{
return BigIntegers.AsUnsignedByteArray(agreementValue);
}
- return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
+ return BigIntegers.AsUnsignedByteArray(mBasicAgreement.GetFieldSize(), agreementValue);
}
}
}
diff --git a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
index 18b23a67b..7c4213c25 100644
--- a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
+++ b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -1,53 +1,146 @@
using System;
using System.IO;
-using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
namespace Org.BouncyCastle.Crypto.Tls
{
public class DefaultTlsCipherFactory
- : TlsCipherFactory
+ : AbstractTlsCipherFactory
{
- public virtual TlsCipher CreateCipher(TlsClientContext context,
- int encryptionAlgorithm, DigestAlgorithm digestAlgorithm)
+ /// <exception cref="IOException"></exception>
+ public override TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
{
switch (encryptionAlgorithm)
{
- case EncryptionAlgorithm.cls_3DES_EDE_CBC:
- return CreateDesEdeCipher(context, 24, digestAlgorithm);
- case EncryptionAlgorithm.AES_128_CBC:
- return CreateAesCipher(context, 16, digestAlgorithm);
- case EncryptionAlgorithm.AES_256_CBC:
- return CreateAesCipher(context, 32, digestAlgorithm);
- case EncryptionAlgorithm.RC4_128:
- return CreateRC4Cipher(context, 16, digestAlgorithm);
- default:
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case EncryptionAlgorithm.cls_3DES_EDE_CBC:
+ return CreateDesEdeCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305:
+ // NOTE: Ignores macAlgorithm
+ return CreateChaCha20Poly1305(context);
+ case EncryptionAlgorithm.AES_128_CBC:
+ return CreateAESCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.AES_128_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 16, 16);
+ case EncryptionAlgorithm.AES_128_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 16, 8);
+ case EncryptionAlgorithm.AES_256_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 32, 16);
+ case EncryptionAlgorithm.AES_256_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 32, 8);
+ case EncryptionAlgorithm.AES_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Gcm(context, 16, 16);
+ case EncryptionAlgorithm.AES_256_CBC:
+ return CreateAESCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.AES_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Gcm(context, 32, 16);
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ return CreateCamelliaCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Camellia_Gcm(context, 16, 16);
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ return CreateCamelliaCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Camellia_Gcm(context, 32, 16);
+ case EncryptionAlgorithm.ESTREAM_SALSA20:
+ return CreateSalsa20Cipher(context, 12, 32, macAlgorithm);
+ case EncryptionAlgorithm.NULL:
+ return CreateNullCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.RC4_128:
+ return CreateRC4Cipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.SALSA20:
+ return CreateSalsa20Cipher(context, 20, 32, macAlgorithm);
+ case EncryptionAlgorithm.SEED_CBC:
+ return CreateSeedCipher(context, macAlgorithm);
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
/// <exception cref="IOException"></exception>
- protected virtual TlsCipher CreateRC4Cipher(TlsClientContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm)
+ protected virtual TlsBlockCipher CreateAESCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
{
- return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize);
}
/// <exception cref="IOException"></exception>
- protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize,
- DigestAlgorithm digestAlgorithm)
+ protected virtual TlsBlockCipher CreateCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
{
- return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
- CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ return new TlsBlockCipher(context, CreateCamelliaBlockCipher(),
+ CreateCamelliaBlockCipher(), CreateHMacDigest(macAlgorithm),
+ CreateHMacDigest(macAlgorithm), cipherKeySize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsCipher CreateChaCha20Poly1305(TlsContext context)
+ {
+ return new Chacha20Poly1305(context);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsContext context, int cipherKeySize, int macSize)
+ {
+ return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Ccm(),
+ CreateAeadBlockCipher_Aes_Ccm(), cipherKeySize, macSize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsContext context, int cipherKeySize, int macSize)
+ {
+ return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Gcm(),
+ CreateAeadBlockCipher_Aes_Gcm(), cipherKeySize, macSize);
}
/// <exception cref="IOException"></exception>
- protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize,
- DigestAlgorithm digestAlgorithm)
+ protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsContext context, int cipherKeySize, int macSize)
+ {
+ return new TlsAeadCipher(context, CreateAeadBlockCipher_Camellia_Gcm(),
+ CreateAeadBlockCipher_Camellia_Gcm(), cipherKeySize, macSize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsBlockCipher CreateDesEdeCipher(TlsContext context, int macAlgorithm)
{
return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(),
- CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 24);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsNullCipher CreateNullCipher(TlsContext context, int macAlgorithm)
+ {
+ return new TlsNullCipher(context, CreateHMacDigest(macAlgorithm),
+ CreateHMacDigest(macAlgorithm));
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsStreamCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm)
+ {
+ return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, false);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsStreamCipher CreateSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm)
+ {
+ return new TlsStreamCipher(context, CreateSalsa20StreamCipher(rounds), CreateSalsa20StreamCipher(rounds),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, true);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsBlockCipher CreateSeedCipher(TlsContext context, int macAlgorithm)
+ {
+ return new TlsBlockCipher(context, CreateSeedBlockCipher(), CreateSeedBlockCipher(),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 16);
}
protected virtual IBlockCipher CreateAesEngine()
@@ -55,11 +148,38 @@ namespace Org.BouncyCastle.Crypto.Tls
return new AesEngine();
}
+ protected virtual IBlockCipher CreateCamelliaEngine()
+ {
+ return new CamelliaEngine();
+ }
+
protected virtual IBlockCipher CreateAesBlockCipher()
{
return new CbcBlockCipher(CreateAesEngine());
}
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm()
+ {
+ return new CcmBlockCipher(CreateAesEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm()
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GcmBlockCipher(CreateAesEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm()
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GcmBlockCipher(CreateCamelliaEngine());
+ }
+
+ protected virtual IBlockCipher CreateCamelliaBlockCipher()
+ {
+ return new CbcBlockCipher(CreateCamelliaEngine());
+ }
+
protected virtual IBlockCipher CreateDesEdeBlockCipher()
{
return new CbcBlockCipher(new DesEdeEngine());
@@ -70,21 +190,35 @@ namespace Org.BouncyCastle.Crypto.Tls
return new RC4Engine();
}
+ protected virtual IStreamCipher CreateSalsa20StreamCipher(int rounds)
+ {
+ return new Salsa20Engine(rounds);
+ }
+
+ protected virtual IBlockCipher CreateSeedBlockCipher()
+ {
+ return new CbcBlockCipher(new SeedEngine());
+ }
+
/// <exception cref="IOException"></exception>
- protected virtual IDigest CreateDigest(DigestAlgorithm digestAlgorithm)
+ protected virtual IDigest CreateHMacDigest(int macAlgorithm)
{
- switch (digestAlgorithm)
+ switch (macAlgorithm)
{
- case DigestAlgorithm.MD5:
- return new MD5Digest();
- case DigestAlgorithm.SHA:
- return new Sha1Digest();
- case DigestAlgorithm.SHA256:
- return new Sha256Digest();
- case DigestAlgorithm.SHA384:
- return new Sha384Digest();
- default:
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case MacAlgorithm.cls_null:
+ return null;
+ case MacAlgorithm.hmac_md5:
+ return TlsUtilities.CreateHash(HashAlgorithm.md5);
+ case MacAlgorithm.hmac_sha1:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha1);
+ case MacAlgorithm.hmac_sha256:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha256);
+ case MacAlgorithm.hmac_sha384:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha384);
+ case MacAlgorithm.hmac_sha512:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha512);
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
}
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index d59fae164..a2a04a33c 100644
--- a/crypto/src/crypto/tls/DefaultTlsClient.cs
+++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -12,272 +12,450 @@ using Org.BouncyCastle.Crypto.Parameters;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class DefaultTlsClient
- : TlsClient
+ : AbstractTlsClient
{
- protected TlsCipherFactory cipherFactory;
-
- protected TlsClientContext context;
-
- protected byte selectedCompressionMethod;
- protected int selectedCipherSuite;
-
public DefaultTlsClient()
- : this(new DefaultTlsCipherFactory())
+ : base()
{
}
public DefaultTlsClient(TlsCipherFactory cipherFactory)
+ : base(cipherFactory)
{
- this.cipherFactory = cipherFactory;
- }
-
- public virtual void Init(TlsClientContext context)
- {
- this.context = context;
}
- public virtual bool ShouldUseGmtUnixTime()
+ public override int[] GetCipherSuites()
{
- /*
- * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
- * TLS implementors MUST by default set the entire value the ClientHello.Random and
- * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
- * sequence.
- */
- return false;
- }
-
- public virtual int[] GetCipherSuites()
- {
- return new int[] {
- CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_RSA_WITH_RC4_128_SHA,
};
}
- public virtual byte[] GetCompressionMethods()
+ public override TlsKeyExchange GetKeyExchange()
{
- /*
- * To offer DEFLATE compression, override this method:
- * return new byte[] { CompressionMethod.DEFLATE, CompressionMethod.NULL };
- */
-
- return new byte[] { CompressionMethod.NULL };
- }
-
- public virtual IDictionary GetClientExtensions()
- {
- return null;
- }
-
- public virtual void NotifySessionID(byte[] sessionID)
- {
- // Currently ignored
- }
-
- public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
- {
- this.selectedCipherSuite = selectedCipherSuite;
- }
-
- public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
- {
- this.selectedCompressionMethod = selectedCompressionMethod;
- }
-
- public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
- {
- if (!secureRenegotiation)
+ switch (mSelectedCipherSuite)
{
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return CreateRsaKeyExchange();
+
+ default:
/*
- * RFC 5746 3.4.
- * If the extension is not present, the server does not support
- * secure renegotiation; set secure_renegotiation flag to FALSE.
- * In this case, some clients may want to terminate the handshake
- * instead of continuing; see Section 4.1 for discussion.
- */
-// throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- }
-
- public virtual void ProcessServerExtensions(IDictionary serverExtensions)
- {
- }
-
- public virtual TlsKeyExchange GetKeyExchange()
- {
- switch (selectedCipherSuite)
- {
- case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
- return CreateRsaKeyExchange();
-
- case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
- return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
-
- case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
- return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
-
- case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
- return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
-
- case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
- return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
-
- case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
- return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
-
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
- return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
-
- case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
- return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
-
- case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
*/
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
-
- public abstract TlsAuthentication GetAuthentication();
-
- public virtual TlsCompression GetCompression()
- {
- switch (selectedCompressionMethod)
- {
- case CompressionMethod.NULL:
- return new TlsNullCompression();
-
- case CompressionMethod.DEFLATE:
- return new TlsDeflateCompression();
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected compression method was in the list of client-offered compression
- * methods, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
- public virtual TlsCipher GetCipher()
+ public override TlsCipher GetCipher()
{
- switch (selectedCipherSuite)
+ switch (mSelectedCipherSuite)
{
- case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
*/
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange)
{
- return new TlsDHKeyExchange(context, keyExchange);
+ return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null);
}
protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange)
{
- return new TlsDheKeyExchange(context, keyExchange);
+ return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null);
}
protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
{
- return new TlsECDHKeyExchange(context, keyExchange);
+ return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+ mServerECPointFormats);
}
protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange)
{
- return new TlsECDheKeyExchange(context, keyExchange);
+ return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+ mServerECPointFormats);
}
protected virtual TlsKeyExchange CreateRsaKeyExchange()
{
- return new TlsRsaKeyExchange(context);
+ return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms);
}
}
}
diff --git a/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs
new file mode 100644
index 000000000..34d15d146
--- /dev/null
+++ b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class DefaultTlsEncryptionCredentials
+ : AbstractTlsEncryptionCredentials
+ {
+ protected readonly TlsContext mContext;
+ protected readonly Certificate mCertificate;
+ protected readonly AsymmetricKeyParameter mPrivateKey;
+
+ public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate,
+ AsymmetricKeyParameter privateKey)
+ {
+ if (certificate == null)
+ throw new ArgumentNullException("certificate");
+ if (certificate.IsEmpty)
+ throw new ArgumentException("cannot be empty", "certificate");
+ if (privateKey == null)
+ throw new ArgumentNullException("'privateKey' cannot be null");
+ if (!privateKey.IsPrivate)
+ throw new ArgumentException("must be private", "privateKey");
+
+ if (privateKey is RsaKeyParameters)
+ {
+ }
+ else
+ {
+ throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey");
+ }
+
+ this.mContext = context;
+ this.mCertificate = certificate;
+ this.mPrivateKey = privateKey;
+ }
+
+ public override Certificate Certificate
+ {
+ get { return mCertificate; }
+ }
+
+ /// <exception cref="IOException"></exception>
+ public override byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret)
+ {
+ return TlsRsaUtilities.SafeDecryptPreMasterSecret(mContext, (RsaKeyParameters)mPrivateKey, encryptedPreMasterSecret);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsServer.cs b/crypto/src/crypto/tls/DefaultTlsServer.cs
new file mode 100644
index 000000000..017ed0d85
--- /dev/null
+++ b/crypto/src/crypto/tls/DefaultTlsServer.cs
@@ -0,0 +1,548 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class DefaultTlsServer
+ : AbstractTlsServer
+ {
+ public DefaultTlsServer()
+ : base()
+ {
+ }
+
+ public DefaultTlsServer(TlsCipherFactory cipherFactory)
+ : base(cipherFactory)
+ {
+ }
+
+ protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected virtual TlsSignerCredentials GetRsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected virtual DHParameters GetDHParameters()
+ {
+ return DHStandardGroups.rfc5114_1024_160;
+ }
+
+ protected override int[] GetCipherSuites()
+ {
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ };
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return GetRsaEncryptionCredentials();
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ return GetRsaSignerCredentials();
+
+ default:
+ /*
+ * Note: internal error here; selected a key exchange we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public override TlsKeyExchange GetKeyExchange()
+ {
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return createRSAKeyExchange();
+
+ default:
+ /*
+ * Note: internal error here; selected a key exchange we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public override TlsCipher GetCipher()
+ {
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; selected a cipher suite we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange)
+ {
+ return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters());
+ }
+
+ protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange)
+ {
+ return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters());
+ }
+
+ protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
+ {
+ return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+ mServerECPointFormats);
+ }
+
+ protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange)
+ {
+ return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+ mServerECPointFormats);
+ }
+
+ protected virtual TlsKeyExchange createRSAKeyExchange()
+ {
+ return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
index 2c5aa3524..c7a136573 100644
--- a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
+++ b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -1,76 +1,92 @@
using System;
+using System.IO;
using Org.BouncyCastle.Crypto.Parameters;
namespace Org.BouncyCastle.Crypto.Tls
{
public class DefaultTlsSignerCredentials
- : TlsSignerCredentials
+ : AbstractTlsSignerCredentials
{
- protected TlsClientContext context;
- protected Certificate clientCert;
- protected AsymmetricKeyParameter clientPrivateKey;
+ protected readonly TlsContext mContext;
+ protected readonly Certificate mCertificate;
+ protected readonly AsymmetricKeyParameter mPrivateKey;
+ protected readonly SignatureAndHashAlgorithm mSignatureAndHashAlgorithm;
- protected TlsSigner clientSigner;
+ protected readonly TlsSigner mSigner;
- public DefaultTlsSignerCredentials(TlsClientContext context,
- Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+ public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey)
+ : this(context, certificate, privateKey, null)
{
- if (clientCertificate == null)
- {
- throw new ArgumentNullException("clientCertificate");
- }
- if (clientCertificate.Length == 0)
- {
+ }
+
+ public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ if (certificate == null)
+ throw new ArgumentNullException("certificate");
+ if (certificate.IsEmpty)
throw new ArgumentException("cannot be empty", "clientCertificate");
- }
- if (clientPrivateKey == null)
- {
- throw new ArgumentNullException("clientPrivateKey");
- }
- if (!clientPrivateKey.IsPrivate)
- {
- throw new ArgumentException("must be private", "clientPrivateKey");
- }
+ if (privateKey == null)
+ throw new ArgumentNullException("privateKey");
+ if (!privateKey.IsPrivate)
+ throw new ArgumentException("must be private", "privateKey");
+ if (TlsUtilities.IsTlsV12(context) && signatureAndHashAlgorithm == null)
+ throw new ArgumentException("cannot be null for (D)TLS 1.2+", "signatureAndHashAlgorithm");
- if (clientPrivateKey is RsaKeyParameters)
+ if (privateKey is RsaKeyParameters)
{
- clientSigner = new TlsRsaSigner();
+ mSigner = new TlsRsaSigner();
}
- else if (clientPrivateKey is DsaPrivateKeyParameters)
+ else if (privateKey is DsaPrivateKeyParameters)
{
- clientSigner = new TlsDssSigner();
+ mSigner = new TlsDssSigner();
}
- else if (clientPrivateKey is ECPrivateKeyParameters)
+ else if (privateKey is ECPrivateKeyParameters)
{
- clientSigner = new TlsECDsaSigner();
+ mSigner = new TlsECDsaSigner();
}
else
{
- throw new ArgumentException("type not supported: "
- + clientPrivateKey.GetType().FullName, "clientPrivateKey");
+ throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey");
}
- this.context = context;
- this.clientCert = clientCertificate;
- this.clientPrivateKey = clientPrivateKey;
+ this.mSigner.Init(context);
+
+ this.mContext = context;
+ this.mCertificate = certificate;
+ this.mPrivateKey = privateKey;
+ this.mSignatureAndHashAlgorithm = signatureAndHashAlgorithm;
}
- public virtual Certificate Certificate
+ public override Certificate Certificate
{
- get { return clientCert; }
+ get { return mCertificate; }
}
- public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1)
+ /// <exception cref="IOException"></exception>
+ public override byte[] GenerateCertificateSignature(byte[] hash)
{
try
{
- return clientSigner.GenerateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1);
+ if (TlsUtilities.IsTlsV12(mContext))
+ {
+ return mSigner.GenerateRawSignature(mSignatureAndHashAlgorithm, mPrivateKey, hash);
+ }
+ else
+ {
+ return mSigner.GenerateRawSignature(mPrivateKey, hash);
+ }
}
- catch (CryptoException)
+ catch (CryptoException e)
{
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
+
+ public override SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+ {
+ get { return mSignatureAndHashAlgorithm; }
+ }
}
}
diff --git a/crypto/src/crypto/tls/DeferredHash.cs b/crypto/src/crypto/tls/DeferredHash.cs
new file mode 100644
index 000000000..1112d4a3c
--- /dev/null
+++ b/crypto/src/crypto/tls/DeferredHash.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * Buffers input until the hash algorithm is determined.
+ */
+ internal class DeferredHash
+ : TlsHandshakeHash
+ {
+ protected const int BUFFERING_HASH_LIMIT = 4;
+
+ protected TlsContext mContext;
+
+ private DigestInputBuffer mBuf;
+ private IDictionary mHashes;
+ private int mPrfHashAlgorithm;
+
+ internal DeferredHash()
+ {
+ this.mBuf = new DigestInputBuffer();
+ this.mHashes = Platform.CreateHashtable();
+ this.mPrfHashAlgorithm = -1;
+ }
+
+ private DeferredHash(byte prfHashAlgorithm, IDigest prfHash)
+ {
+ this.mBuf = null;
+ this.mHashes = Platform.CreateHashtable();
+ this.mPrfHashAlgorithm = prfHashAlgorithm;
+ mHashes[prfHashAlgorithm] = prfHash;
+ }
+
+ public virtual void Init(TlsContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual TlsHandshakeHash NotifyPrfDetermined()
+ {
+ int prfAlgorithm = mContext.SecurityParameters.PrfAlgorithm;
+ if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy)
+ {
+ CombinedHash legacyHash = new CombinedHash();
+ legacyHash.Init(mContext);
+ mBuf.UpdateDigest(legacyHash);
+ return legacyHash.NotifyPrfDetermined();
+ }
+
+ this.mPrfHashAlgorithm = TlsUtilities.GetHashAlgorithmForPrfAlgorithm(prfAlgorithm);
+
+ CheckTrackingHash((byte)mPrfHashAlgorithm);
+
+ return this;
+ }
+
+ public virtual void TrackHashAlgorithm(byte hashAlgorithm)
+ {
+ if (mBuf == null)
+ throw new InvalidOperationException("Too late to track more hash algorithms");
+
+ CheckTrackingHash(hashAlgorithm);
+ }
+
+ public virtual void SealHashAlgorithms()
+ {
+ CheckStopBuffering();
+ }
+
+ public virtual TlsHandshakeHash StopTracking()
+ {
+ byte prfHashAlgorithm = (byte)mPrfHashAlgorithm;
+ IDigest prfHash = TlsUtilities.CloneHash(prfHashAlgorithm, (IDigest)mHashes[prfHashAlgorithm]);
+ if (mBuf != null)
+ {
+ mBuf.UpdateDigest(prfHash);
+ }
+ DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash);
+ result.Init(mContext);
+ return result;
+ }
+
+ public virtual IDigest ForkPrfHash()
+ {
+ CheckStopBuffering();
+
+ byte prfHashAlgorithm = (byte)mPrfHashAlgorithm;
+ if (mBuf != null)
+ {
+ IDigest prfHash = TlsUtilities.CreateHash(prfHashAlgorithm);
+ mBuf.UpdateDigest(prfHash);
+ return prfHash;
+ }
+
+ return TlsUtilities.CloneHash(prfHashAlgorithm, (IDigest)mHashes[prfHashAlgorithm]);
+ }
+
+ public virtual byte[] GetFinalHash(byte hashAlgorithm)
+ {
+ IDigest d = (IDigest)mHashes[hashAlgorithm];
+ if (d == null)
+ throw new InvalidOperationException("HashAlgorithm " + hashAlgorithm + " is not being tracked");
+
+ d = TlsUtilities.CloneHash(hashAlgorithm, d);
+ if (mBuf != null)
+ {
+ mBuf.UpdateDigest(d);
+ }
+
+ return DigestUtilities.DoFinal(d);
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { throw new InvalidOperationException("Use Fork() to get a definite IDigest"); }
+ }
+
+ public virtual int GetByteLength()
+ {
+ throw new InvalidOperationException("Use Fork() to get a definite IDigest");
+ }
+
+ public virtual int GetDigestSize()
+ {
+ throw new InvalidOperationException("Use Fork() to get a definite IDigest");
+ }
+
+ public virtual void Update(byte input)
+ {
+ if (mBuf != null)
+ {
+ mBuf.WriteByte(input);
+ return;
+ }
+
+ foreach (IDigest hash in mHashes.Values)
+ {
+ hash.Update(input);
+ }
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ if (mBuf != null)
+ {
+ mBuf.Write(input, inOff, len);
+ return;
+ }
+
+ foreach (IDigest hash in mHashes.Values)
+ {
+ hash.BlockUpdate(input, inOff, len);
+ }
+ }
+
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ throw new InvalidOperationException("Use Fork() to get a definite IDigest");
+ }
+
+ public virtual void Reset()
+ {
+ if (mBuf != null)
+ {
+ mBuf.SetLength(0);
+ return;
+ }
+
+ foreach (IDigest hash in mHashes.Values)
+ {
+ hash.Reset();
+ }
+ }
+
+ protected virtual void CheckStopBuffering()
+ {
+ if (mBuf != null && mHashes.Count <= BUFFERING_HASH_LIMIT)
+ {
+ foreach (IDigest hash in mHashes.Values)
+ {
+ mBuf.UpdateDigest(hash);
+ }
+
+ this.mBuf = null;
+ }
+ }
+
+ protected virtual void CheckTrackingHash(byte hashAlgorithm)
+ {
+ if (!mHashes.Contains(hashAlgorithm))
+ {
+ IDigest hash = TlsUtilities.CreateHash(hashAlgorithm);
+ mHashes[hashAlgorithm] = hash;
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/DigestAlgorithm.cs b/crypto/src/crypto/tls/DigestAlgorithm.cs
deleted file mode 100644
index 745bea448..000000000
--- a/crypto/src/crypto/tls/DigestAlgorithm.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Org.BouncyCastle.Crypto.Tls
-{
- /// <summary>RFC 2246</summary>
- /// <remarks>
- /// Note that the values here are implementation-specific and arbitrary. It is recommended not to
- /// depend on the particular values (e.g. serialization).
- /// </remarks>
- [Obsolete("Use MacAlgorithm constants instead")]
- public enum DigestAlgorithm
- {
- NULL,
- MD5,
- SHA,
-
- /*
- * RFC 5246
- */
- SHA256,
- SHA384,
- SHA512,
- }
-}
diff --git a/crypto/src/crypto/tls/DigitallySigned.cs b/crypto/src/crypto/tls/DigitallySigned.cs
new file mode 100644
index 000000000..8b7344fd9
--- /dev/null
+++ b/crypto/src/crypto/tls/DigitallySigned.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class DigitallySigned
+ {
+ protected readonly SignatureAndHashAlgorithm mAlgorithm;
+ protected readonly byte[] mSignature;
+
+ public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature)
+ {
+ if (signature == null)
+ throw new ArgumentNullException("signature");
+
+ this.mAlgorithm = algorithm;
+ this.mSignature = signature;
+ }
+
+ /**
+ * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2).
+ */
+ public virtual SignatureAndHashAlgorithm Algorithm
+ {
+ get { return mAlgorithm; }
+ }
+
+ public virtual byte[] Signature
+ {
+ get { return mSignature; }
+ }
+
+ /**
+ * Encode this {@link DigitallySigned} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ if (mAlgorithm != null)
+ {
+ mAlgorithm.Encode(output);
+ }
+ TlsUtilities.WriteOpaque16(mSignature, output);
+ }
+
+ /**
+ * Parse a {@link DigitallySigned} from a {@link Stream}.
+ *
+ * @param context
+ * the {@link TlsContext} of the current connection.
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link DigitallySigned} object.
+ * @throws IOException
+ */
+ public static DigitallySigned Parse(TlsContext context, Stream input)
+ {
+ SignatureAndHashAlgorithm algorithm = null;
+ if (TlsUtilities.IsTlsV12(context))
+ {
+ algorithm = SignatureAndHashAlgorithm.Parse(input);
+ }
+ byte[] signature = TlsUtilities.ReadOpaque16(input);
+ return new DigitallySigned(algorithm, signature);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs
index c186d410b..280321e2a 100644
--- a/crypto/src/crypto/tls/ExporterLabel.cs
+++ b/crypto/src/crypto/tls/ExporterLabel.cs
@@ -6,12 +6,6 @@ namespace Org.BouncyCastle.Crypto.Tls
public abstract class ExporterLabel
{
/*
- * BC-specific
- */
- internal const string client_random = "client random";
- internal const string server_random = "server random";
-
- /*
* RFC 5246
*/
public const string client_finished = "client finished";
@@ -34,5 +28,10 @@ namespace Org.BouncyCastle.Crypto.Tls
* RFC 5764
*/
public const string dtls_srtp = "EXTRACTOR-dtls_srtp";
+
+ /*
+ * draft-ietf-tls-session-hash-01
+ */
+ public static readonly string extended_master_secret = "extended master secret";
}
}
diff --git a/crypto/src/crypto/tls/ExtensionType.cs b/crypto/src/crypto/tls/ExtensionType.cs
index 929c134d5..acee380b6 100644
--- a/crypto/src/crypto/tls/ExtensionType.cs
+++ b/crypto/src/crypto/tls/ExtensionType.cs
@@ -44,14 +44,28 @@ namespace Org.BouncyCastle.Crypto.Tls
public const int heartbeat = 15;
/*
+ * RFC 7366
+ */
+ public const int encrypt_then_mac = 22;
+
+ /*
+ * draft-ietf-tls-session-hash-01
+ *
+ * NOTE: Early code-point assignment
+ */
+ public const int extended_master_secret = 23;
+
+ /*
* RFC 5077 7.
*/
public const int session_ticket = 35;
/*
- * draft-ietf-tls-encrypt-then-mac-03
+ * draft-ietf-tls-negotiated-ff-dhe-01
+ *
+ * WARNING: Placeholder value; the real value is TBA
*/
- public const int encrypt_then_mac = 22;
+ public static readonly int negotiated_ff_dhe_groups = 101;
/*
* RFC 5746 3.2.
diff --git a/crypto/src/crypto/tls/FiniteFieldDheGroup.cs b/crypto/src/crypto/tls/FiniteFieldDheGroup.cs
new file mode 100644
index 000000000..437504941
--- /dev/null
+++ b/crypto/src/crypto/tls/FiniteFieldDheGroup.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /*
+ * draft-ietf-tls-negotiated-ff-dhe-01
+ */
+ public abstract class FiniteFieldDheGroup
+ {
+ public const byte ffdhe2432 = 0;
+ public const byte ffdhe3072 = 1;
+ public const byte ffdhe4096 = 2;
+ public const byte ffdhe6144 = 3;
+ public const byte ffdhe8192 = 4;
+
+ public static bool IsValid(byte group)
+ {
+ return group >= ffdhe2432 && group <= ffdhe8192;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/HeartbeatExtension.cs b/crypto/src/crypto/tls/HeartbeatExtension.cs
new file mode 100644
index 000000000..049837266
--- /dev/null
+++ b/crypto/src/crypto/tls/HeartbeatExtension.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class HeartbeatExtension
+ {
+ protected readonly byte mMode;
+
+ public HeartbeatExtension(byte mode)
+ {
+ if (!HeartbeatMode.IsValid(mode))
+ throw new ArgumentException("not a valid HeartbeatMode value", "mode");
+
+ this.mMode = mode;
+ }
+
+ public virtual byte Mode
+ {
+ get { return mMode; }
+ }
+
+ /**
+ * Encode this {@link HeartbeatExtension} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(mMode, output);
+ }
+
+ /**
+ * Parse a {@link HeartbeatExtension} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link HeartbeatExtension} object.
+ * @throws IOException
+ */
+ public static HeartbeatExtension Parse(Stream input)
+ {
+ byte mode = TlsUtilities.ReadUint8(input);
+ if (!HeartbeatMode.IsValid(mode))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return new HeartbeatExtension(mode);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/HeartbeatMessage.cs b/crypto/src/crypto/tls/HeartbeatMessage.cs
new file mode 100644
index 000000000..f64a7baa4
--- /dev/null
+++ b/crypto/src/crypto/tls/HeartbeatMessage.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class HeartbeatMessage
+ {
+ protected readonly byte mType;
+ protected readonly byte[] mPayload;
+ protected readonly int mPaddingLength;
+
+ public HeartbeatMessage(byte type, byte[] payload, int paddingLength)
+ {
+ if (!HeartbeatMessageType.IsValid(type))
+ throw new ArgumentException("not a valid HeartbeatMessageType value", "type");
+ if (payload == null || payload.Length >= (1 << 16))
+ throw new ArgumentException("must have length < 2^16", "payload");
+ if (paddingLength < 16)
+ throw new ArgumentException("must be at least 16", "paddingLength");
+
+ this.mType = type;
+ this.mPayload = payload;
+ this.mPaddingLength = paddingLength;
+ }
+
+ /**
+ * Encode this {@link HeartbeatMessage} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(TlsContext context, Stream output)
+ {
+ TlsUtilities.WriteUint8(mType, output);
+
+ TlsUtilities.CheckUint16(mPayload.Length);
+ TlsUtilities.WriteUint16(mPayload.Length, output);
+ output.Write(mPayload, 0, mPayload.Length);
+
+ byte[] padding = new byte[mPaddingLength];
+ context.NonceRandomGenerator.NextBytes(padding);
+ output.Write(padding, 0, padding.Length);
+ }
+
+ /**
+ * Parse a {@link HeartbeatMessage} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link HeartbeatMessage} object.
+ * @throws IOException
+ */
+ public static HeartbeatMessage Parse(Stream input)
+ {
+ byte type = TlsUtilities.ReadUint8(input);
+ if (!HeartbeatMessageType.IsValid(type))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ int payload_length = TlsUtilities.ReadUint16(input);
+
+ PayloadBuffer buf = new PayloadBuffer();
+ Streams.PipeAll(input, buf);
+
+ byte[] payload = buf.ToTruncatedByteArray(payload_length);
+ if (payload == null)
+ {
+ /*
+ * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the
+ * received HeartbeatMessage MUST be discarded silently.
+ */
+ return null;
+ }
+
+ TlsUtilities.CheckUint16(buf.Length);
+ int padding_length = (int)buf.Length - payload.Length;
+
+ /*
+ * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored
+ */
+ return new HeartbeatMessage(type, payload, padding_length);
+ }
+
+ internal class PayloadBuffer
+ : MemoryStream
+ {
+ internal byte[] ToTruncatedByteArray(int payloadLength)
+ {
+ /*
+ * RFC 6520 4. The padding_length MUST be at least 16.
+ */
+ int minimumCount = payloadLength + 16;
+ if (Length < minimumCount)
+ return null;
+ return Arrays.CopyOf(GetBuffer(), payloadLength);
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/ICertificateVerifyer.cs b/crypto/src/crypto/tls/ICertificateVerifyer.cs
deleted file mode 100644
index df5ea51d7..000000000
--- a/crypto/src/crypto/tls/ICertificateVerifyer.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-
-using Org.BouncyCastle.Asn1.X509;
-
-namespace Org.BouncyCastle.Crypto.Tls
-{
- /// <remarks>
- /// This should be implemented by any class which can find out, if a given
- /// certificate chain is being accepted by an client.
- /// </remarks>
- [Obsolete("Perform certificate verification in TlsAuthentication implementation")]
- public interface ICertificateVerifyer
- {
- /// <param name="certs">The certs, which are part of the chain.</param>
- /// <returns>True, if the chain is accepted, false otherwise</returns>
- bool IsValid(X509CertificateStructure[] certs);
- }
-}
diff --git a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs b/crypto/src/crypto/tls/LegacyTlsAuthentication.cs
deleted file mode 100644
index bce31c0b0..000000000
--- a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-
-namespace Org.BouncyCastle.Crypto.Tls
-{
- /// <summary>
- /// A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication.
- /// </summary>
- [Obsolete]
- public class LegacyTlsAuthentication
- : TlsAuthentication
- {
- protected ICertificateVerifyer verifyer;
-
- public LegacyTlsAuthentication(ICertificateVerifyer verifyer)
- {
- this.verifyer = verifyer;
- }
-
- public virtual void NotifyServerCertificate(Certificate serverCertificate)
- {
- if (!this.verifyer.IsValid(serverCertificate.GetCertificateList()))
- throw new TlsFatalAlert(AlertDescription.user_canceled);
- }
-
- public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
- {
- return null;
- }
- }
-}
diff --git a/crypto/src/crypto/tls/LegacyTlsClient.cs b/crypto/src/crypto/tls/LegacyTlsClient.cs
deleted file mode 100644
index fbb9a732e..000000000
--- a/crypto/src/crypto/tls/LegacyTlsClient.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-
-namespace Org.BouncyCastle.Crypto.Tls
-{
- /// <summary>
- /// A temporary class to use LegacyTlsAuthentication
- /// </summary>
- [Obsolete]
- public class LegacyTlsClient
- : DefaultTlsClient
- {
- [Obsolete]
- protected ICertificateVerifyer verifyer;
-
- [Obsolete]
- public LegacyTlsClient(ICertificateVerifyer verifyer)
- {
- this.verifyer = verifyer;
- }
-
- public override TlsAuthentication GetAuthentication()
- {
- return new LegacyTlsAuthentication(verifyer);
- }
- }
-}
\ No newline at end of file
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index e60688155..6063572a0 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -4,199 +4,258 @@ using System.Collections;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class PskTlsClient
- :TlsClient
+ : AbstractTlsClient
{
- protected TlsCipherFactory cipherFactory;
- protected TlsPskIdentity pskIdentity;
-
- protected TlsClientContext context;
-
- protected byte selectedCompressionMethod;
- protected int selectedCipherSuite;
+ protected TlsPskIdentity mPskIdentity;
public PskTlsClient(TlsPskIdentity pskIdentity)
- : this(new DefaultTlsCipherFactory(), pskIdentity)
+ : this(new DefaultTlsCipherFactory(), pskIdentity)
{
}
public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity)
+ : base(cipherFactory)
{
- this.cipherFactory = cipherFactory;
- this.pskIdentity = pskIdentity;
+ this.mPskIdentity = pskIdentity;
}
- public virtual void Init(TlsClientContext context)
+ public override int[] GetCipherSuites()
{
- this.context = context;
- }
-
- public virtual bool ShouldUseGmtUnixTime()
- {
- /*
- * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
- * TLS implementors MUST by default set the entire value the ClientHello.Random and
- * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
- * sequence.
- */
- return false;
- }
-
- public virtual int[] GetCipherSuites()
- {
- return new int[] {
- CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA,
- CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA,
- CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_PSK_WITH_RC4_128_SHA,
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA
};
}
- public virtual IDictionary GetClientExtensions()
- {
- return null;
- }
-
- public virtual byte[] GetCompressionMethods()
+ public override TlsKeyExchange GetKeyExchange()
{
- return new byte[] { CompressionMethod.NULL };
- }
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
- public virtual void NotifySessionID(byte[] sessionID)
- {
- // Currently ignored
- }
+ case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
- public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
- {
- this.selectedCipherSuite = selectedCipherSuite;
- }
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
- public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
- {
- this.selectedCompressionMethod = selectedCompressionMethod;
- }
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
- public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
- {
- if (!secureRenegotiation)
- {
+ default:
/*
- * RFC 5746 3.4. If the extension is not present, the server does not support
- * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
- * some clients may want to terminate the handshake instead of continuing; see
- * Section 4.1 for discussion.
- */
-// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
- public virtual void ProcessServerExtensions(IDictionary serverExtensions)
- {
- }
-
- public virtual TlsKeyExchange GetKeyExchange()
+ public override TlsCipher GetCipher()
{
- switch (selectedCipherSuite)
+ switch (mSelectedCipherSuite)
{
- case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
- return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
-
- case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
- return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
-
- case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
- return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
- public abstract TlsAuthentication GetAuthentication();
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
- public virtual TlsCompression GetCompression()
- {
- switch (selectedCompressionMethod)
- {
- case CompressionMethod.NULL:
- return new TlsNullCompression();
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected compression method was in the list of client-offered compression
- * methods, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
- public virtual TlsCipher GetCipher()
- {
- switch (selectedCipherSuite)
- {
- case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
- DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
- DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
- DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128,
- DigestAlgorithm.SHA);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
{
- return new TlsPskKeyExchange(context, keyExchange, pskIdentity);
+ return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, mNamedCurves,
+ mClientECPointFormats, mServerECPointFormats);
}
}
}
diff --git a/crypto/src/crypto/tls/RecordStream.cs b/crypto/src/crypto/tls/RecordStream.cs
index ce8882cbe..db5b158bc 100644
--- a/crypto/src/crypto/tls/RecordStream.cs
+++ b/crypto/src/crypto/tls/RecordStream.cs
@@ -3,164 +3,331 @@ using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <remarks>An implementation of the TLS 1.0 record layer.</remarks>
+ /// <summary>An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3.</summary>
internal class RecordStream
{
- private TlsProtocolHandler handler;
- private Stream inStr;
- private Stream outStr;
- private CombinedHash hash;
- private TlsCompression readCompression = null;
- private TlsCompression writeCompression = null;
- private TlsCipher readCipher = null;
- private TlsCipher writeCipher = null;
- private MemoryStream buffer = new MemoryStream();
+ private const int DEFAULT_PLAINTEXT_LIMIT = (1 << 14);
- internal RecordStream(
- TlsProtocolHandler handler,
- Stream inStr,
- Stream outStr)
+ private TlsProtocol mHandler;
+ private Stream mInput;
+ private Stream mOutput;
+ private TlsCompression mPendingCompression = null, mReadCompression = null, mWriteCompression = null;
+ private TlsCipher mPendingCipher = null, mReadCipher = null, mWriteCipher = null;
+ private long mReadSeqNo = 0, mWriteSeqNo = 0;
+ private MemoryStream mBuffer = new MemoryStream();
+
+ private TlsHandshakeHash mHandshakeHash = null;
+
+ private ProtocolVersion mReadVersion = null, mWriteVersion = null;
+ private bool mRestrictReadVersion = true;
+
+ private int mPlaintextLimit, mCompressedLimit, mCiphertextLimit;
+
+ internal RecordStream(TlsProtocol handler, Stream input, Stream output)
+ {
+ this.mHandler = handler;
+ this.mInput = input;
+ this.mOutput = output;
+ this.mReadCompression = new TlsNullCompression();
+ this.mWriteCompression = this.mReadCompression;
+ }
+
+ internal virtual void Init(TlsContext context)
+ {
+ this.mReadCipher = new TlsNullCipher(context);
+ this.mWriteCipher = this.mReadCipher;
+ this.mHandshakeHash = new DeferredHash();
+ this.mHandshakeHash.Init(context);
+
+ SetPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT);
+ }
+
+ internal virtual int GetPlaintextLimit()
{
- this.handler = handler;
- this.inStr = inStr;
- this.outStr = outStr;
- this.hash = new CombinedHash();
- this.readCompression = new TlsNullCompression();
- this.writeCompression = this.readCompression;
- this.readCipher = new TlsNullCipher();
- this.writeCipher = this.readCipher;
+ return mPlaintextLimit;
}
- internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher)
+ internal virtual void SetPlaintextLimit(int plaintextLimit)
{
- this.writeCompression = tlsCompression;
- this.writeCipher = tlsCipher;
+ this.mPlaintextLimit = plaintextLimit;
+ this.mCompressedLimit = this.mPlaintextLimit + 1024;
+ this.mCiphertextLimit = this.mCompressedLimit + 1024;
}
- internal void ServerClientSpecReceived()
+ internal virtual ProtocolVersion ReadVersion
{
- this.readCompression = this.writeCompression;
- this.readCipher = this.writeCipher;
+ get { return mReadVersion; }
+ set { this.mReadVersion = value; }
}
- public void ReadData()
+ internal virtual void SetWriteVersion(ProtocolVersion writeVersion)
{
- byte contentType = TlsUtilities.ReadUint8(inStr);
- TlsUtilities.CheckVersion(inStr);
- int size = TlsUtilities.ReadUint16(inStr);
- byte[] buf = DecodeAndVerify(contentType, inStr, size);
- handler.ProcessData(contentType, buf, 0, buf.Length);
+ this.mWriteVersion = writeVersion;
}
- internal byte[] DecodeAndVerify(
- byte contentType,
- Stream inStr,
- int len)
+ /**
+ * RFC 5246 E.1. "Earlier versions of the TLS specification were not fully clear on what the
+ * record layer version number (TLSPlaintext.version) should contain when sending ClientHello
+ * (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers
+ * compliant with this specification MUST accept any value {03,XX} as the record layer version
+ * number for ClientHello."
+ */
+ internal virtual void SetRestrictReadVersion(bool enabled)
{
- byte[] buf = new byte[len];
- TlsUtilities.ReadFully(buf, inStr);
- byte[] decoded = readCipher.DecodeCiphertext(contentType, buf, 0, buf.Length);
+ this.mRestrictReadVersion = enabled;
+ }
- Stream cOut = readCompression.Decompress(buffer);
+ internal virtual void SetPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher)
+ {
+ this.mPendingCompression = tlsCompression;
+ this.mPendingCipher = tlsCipher;
+ }
+
+ internal virtual void SentWriteCipherSpec()
+ {
+ if (mPendingCompression == null || mPendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ this.mWriteCompression = this.mPendingCompression;
+ this.mWriteCipher = this.mPendingCipher;
+ this.mWriteSeqNo = 0;
+ }
- if (cOut == buffer)
+ internal virtual void ReceivedReadCipherSpec()
+ {
+ if (mPendingCompression == null || mPendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ this.mReadCompression = this.mPendingCompression;
+ this.mReadCipher = this.mPendingCipher;
+ this.mReadSeqNo = 0;
+ }
+
+ internal virtual void FinaliseHandshake()
+ {
+ if (mReadCompression != mPendingCompression || mWriteCompression != mPendingCompression
+ || mReadCipher != mPendingCipher || mWriteCipher != mPendingCipher)
{
- return decoded;
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
+ this.mPendingCompression = null;
+ this.mPendingCipher = null;
+ }
- cOut.Write(decoded, 0, decoded.Length);
- cOut.Flush();
- byte[] contents = buffer.ToArray();
- buffer.SetLength(0);
- return contents;
+ internal virtual bool ReadRecord()
+ {
+ byte[] recordHeader = TlsUtilities.ReadAllOrNothing(5, mInput);
+ if (recordHeader == null)
+ return false;
+
+ byte type = TlsUtilities.ReadUint8(recordHeader, 0);
+
+ /*
+ * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an
+ * unexpected_message alert.
+ */
+ CheckType(type, AlertDescription.unexpected_message);
+
+ if (!mRestrictReadVersion)
+ {
+ int version = TlsUtilities.ReadVersionRaw(recordHeader, 1);
+ if ((version & 0xffffff00) != 0x0300)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ else
+ {
+ ProtocolVersion version = TlsUtilities.ReadVersion(recordHeader, 1);
+ if (mReadVersion == null)
+ {
+ mReadVersion = version;
+ }
+ else if (!version.Equals(mReadVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ int length = TlsUtilities.ReadUint16(recordHeader, 3);
+ byte[] plaintext = DecodeAndVerify(type, mInput, length);
+ mHandler.ProcessRecord(type, plaintext, 0, plaintext.Length);
+ return true;
}
- internal void WriteMessage(
- byte type,
- byte[] message,
- int offset,
- int len)
+ internal virtual byte[] DecodeAndVerify(byte type, Stream input, int len)
{
+ CheckLength(len, mCiphertextLimit, AlertDescription.record_overflow);
+
+ byte[] buf = TlsUtilities.ReadFully(len, input);
+ byte[] decoded = mReadCipher.DecodeCiphertext(mReadSeqNo++, type, buf, 0, buf.Length);
+
+ CheckLength(decoded.Length, mCompressedLimit, AlertDescription.record_overflow);
+
+ /*
+ * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for
+ * ensuring that messages cannot cause internal buffer overflows.
+ */
+ Stream cOut = mReadCompression.Decompress(mBuffer);
+ if (cOut != mBuffer)
+ {
+ cOut.Write(decoded, 0, decoded.Length);
+ cOut.Flush();
+ decoded = GetBufferContents();
+ }
+
+ /*
+ * RFC 5264 6.2.2. If the decompression function encounters a TLSCompressed.fragment that
+ * would decompress to a length in excess of 2^14 bytes, it should report a fatal
+ * decompression failure error.
+ */
+ CheckLength(decoded.Length, mPlaintextLimit, AlertDescription.decompression_failure);
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (decoded.Length < 1 && type != ContentType.application_data)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return decoded;
+ }
+
+ internal virtual void WriteRecord(byte type, byte[] plaintext, int plaintextOffset, int plaintextLength)
+ {
+ // Never send anything until a valid ClientHello has been received
+ if (mWriteVersion == null)
+ return;
+
+ /*
+ * RFC 5264 6. Implementations MUST NOT send record types not defined in this document
+ * unless negotiated by some extension.
+ */
+ CheckType(type, AlertDescription.internal_error);
+
+ /*
+ * RFC 5264 6.2.1 The length should not exceed 2^14.
+ */
+ CheckLength(plaintextLength, mPlaintextLimit, AlertDescription.internal_error);
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (plaintextLength < 1 && type != ContentType.application_data)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
if (type == ContentType.handshake)
{
- UpdateHandshakeData(message, offset, len);
+ UpdateHandshakeData(plaintext, plaintextOffset, plaintextLength);
}
- Stream cOut = writeCompression.Compress(buffer);
+ Stream cOut = mWriteCompression.Compress(mBuffer);
byte[] ciphertext;
- if (cOut == buffer)
+ if (cOut == mBuffer)
{
- ciphertext = writeCipher.EncodePlaintext(type, message, offset, len);
+ ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, plaintext, plaintextOffset, plaintextLength);
}
else
{
- cOut.Write(message, offset, len);
+ cOut.Write(plaintext, plaintextOffset, plaintextLength);
cOut.Flush();
- ciphertext = writeCipher.EncodePlaintext(type, buffer.GetBuffer(), 0, (int)buffer.Position);
- buffer.SetLength(0);
+ byte[] compressed = GetBufferContents();
+
+ /*
+ * RFC5264 6.2.2. Compression must be lossless and may not increase the content length
+ * by more than 1024 bytes.
+ */
+ CheckLength(compressed.Length, plaintextLength + 1024, AlertDescription.internal_error);
+
+ ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, compressed, 0, compressed.Length);
}
- byte[] writeMessage = new byte[ciphertext.Length + 5];
- TlsUtilities.WriteUint8((byte)type, writeMessage, 0);
- TlsUtilities.WriteVersion(writeMessage, 1);
- TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3);
- Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length);
- outStr.Write(writeMessage, 0, writeMessage.Length);
- outStr.Flush();
+ /*
+ * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048.
+ */
+ CheckLength(ciphertext.Length, mCiphertextLimit, AlertDescription.internal_error);
+
+ byte[] record = new byte[ciphertext.Length + 5];
+ TlsUtilities.WriteUint8(type, record, 0);
+ TlsUtilities.WriteVersion(mWriteVersion, record, 1);
+ TlsUtilities.WriteUint16(ciphertext.Length, record, 3);
+ Array.Copy(ciphertext, 0, record, 5, ciphertext.Length);
+ mOutput.Write(record, 0, record.Length);
+ mOutput.Flush();
}
- internal void UpdateHandshakeData(
- byte[] message,
- int offset,
- int len)
+ internal virtual void NotifyHelloComplete()
{
- hash.BlockUpdate(message, offset, len);
+ this.mHandshakeHash = mHandshakeHash.NotifyPrfDetermined();
}
- internal byte[] GetCurrentHash()
+ internal virtual TlsHandshakeHash HandshakeHash
{
- return DoFinal(new CombinedHash(hash));
+ get { return mHandshakeHash; }
}
- internal void Close()
+ internal virtual TlsHandshakeHash PrepareToFinish()
+ {
+ TlsHandshakeHash result = mHandshakeHash;
+ this.mHandshakeHash = mHandshakeHash.StopTracking();
+ return result;
+ }
+
+ internal virtual void UpdateHandshakeData(byte[] message, int offset, int len)
+ {
+ mHandshakeHash.BlockUpdate(message, offset, len);
+ }
+
+ internal virtual void SafeClose()
{
- IOException e = null;
try
{
- inStr.Close();
+ mInput.Close();
}
- catch (IOException ex)
+ catch (IOException)
{
- e = ex;
}
try
{
- // NB: This is harmless if outStr == inStr
- outStr.Close();
+ mOutput.Close();
}
- catch (IOException ex)
+ catch (IOException)
{
- e = ex;
}
+ }
- if (e != null)
- {
- throw e;
- }
+ internal virtual void Flush()
+ {
+ mOutput.Flush();
+ }
+
+ private byte[] GetBufferContents()
+ {
+ byte[] contents = mBuffer.ToArray();
+ mBuffer.SetLength(0);
+ return contents;
}
- internal void Flush()
+ private static void CheckType(byte type, byte alertDescription)
{
- outStr.Flush();
+ switch (type)
+ {
+ case ContentType.application_data:
+ case ContentType.alert:
+ case ContentType.change_cipher_spec:
+ case ContentType.handshake:
+ case ContentType.heartbeat:
+ break;
+ default:
+ throw new TlsFatalAlert(alertDescription);
+ }
}
- private static byte[] DoFinal(CombinedHash ch)
+ private static void CheckLength(int length, int limit, byte alertDescription)
{
- byte[] bs = new byte[ch.GetDigestSize()];
- ch.DoFinal(bs, 0);
- return bs;
+ if (length > limit)
+ throw new TlsFatalAlert(alertDescription);
}
}
}
diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 9ed3969eb..12bb59f22 100644
--- a/crypto/src/crypto/tls/SecurityParameters.cs
+++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -1,26 +1,91 @@
using System;
+using Org.BouncyCastle.Utilities;
+
namespace Org.BouncyCastle.Crypto.Tls
{
- public class SecurityParameters
- {
- internal byte[] clientRandom = null;
- internal byte[] serverRandom = null;
- internal byte[] masterSecret = null;
-
- public byte[] ClientRandom
- {
- get { return clientRandom; }
- }
-
- public byte[] ServerRandom
- {
- get { return serverRandom; }
- }
-
- public byte[] MasterSecret
- {
- get { return masterSecret; }
- }
- }
+ public class SecurityParameters
+ {
+ internal int entity = -1;
+ internal int cipherSuite = -1;
+ internal byte compressionAlgorithm = CompressionMethod.cls_null;
+ internal int prfAlgorithm = -1;
+ internal int verifyDataLength = -1;
+ internal byte[] masterSecret = null;
+ internal byte[] clientRandom = null;
+ internal byte[] serverRandom = null;
+ internal byte[] sessionHash = null;
+
+ // TODO Keep these internal, since it's maybe not the ideal place for them
+ internal short maxFragmentLength = -1;
+ internal bool truncatedHMac = false;
+ internal bool encryptThenMac = false;
+ internal bool extendedMasterSecret = false;
+
+ internal virtual void Clear()
+ {
+ if (this.masterSecret != null)
+ {
+ Arrays.Fill(this.masterSecret, (byte)0);
+ this.masterSecret = null;
+ }
+ }
+
+ /**
+ * @return {@link ConnectionEnd}
+ */
+ public virtual int Entity
+ {
+ get { return entity; }
+ }
+
+ /**
+ * @return {@link CipherSuite}
+ */
+ public virtual int CipherSuite
+ {
+ get { return cipherSuite; }
+ }
+
+ /**
+ * @return {@link CompressionMethod}
+ */
+ public byte CompressionAlgorithm
+ {
+ get { return compressionAlgorithm; }
+ }
+
+ /**
+ * @return {@link PRFAlgorithm}
+ */
+ public virtual int PrfAlgorithm
+ {
+ get { return prfAlgorithm; }
+ }
+
+ public virtual int VerifyDataLength
+ {
+ get { return verifyDataLength; }
+ }
+
+ public virtual byte[] MasterSecret
+ {
+ get { return masterSecret; }
+ }
+
+ public virtual byte[] ClientRandom
+ {
+ get { return clientRandom; }
+ }
+
+ public virtual byte[] ServerRandom
+ {
+ get { return serverRandom; }
+ }
+
+ public virtual byte[] SessionHash
+ {
+ get { return sessionHash; }
+ }
+ }
}
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/ServerName.cs b/crypto/src/crypto/tls/ServerName.cs
new file mode 100644
index 000000000..3d1e8f844
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerName.cs
@@ -0,0 +1,105 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class ServerName
+ {
+ protected readonly byte mNameType;
+ protected readonly object mName;
+
+ public ServerName(byte nameType, object name)
+ {
+ if (!IsCorrectType(nameType, name))
+ throw new ArgumentException("not an instance of the correct type", "name");
+
+ this.mNameType = nameType;
+ this.mName = name;
+ }
+
+ public virtual byte NameType
+ {
+ get { return mNameType; }
+ }
+
+ public virtual object Name
+ {
+ get { return mName; }
+ }
+
+ public virtual string GetHostName()
+ {
+ if (!IsCorrectType(Tls.NameType.host_name, mName))
+ throw new InvalidOperationException("'name' is not a HostName string");
+
+ return (string)mName;
+ }
+
+ /**
+ * Encode this {@link ServerName} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(mNameType, output);
+
+ switch (mNameType)
+ {
+ case Tls.NameType.host_name:
+ byte[] utf8Encoding = Strings.ToUtf8ByteArray((string)mName);
+ if (utf8Encoding.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ TlsUtilities.WriteOpaque16(utf8Encoding, output);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /**
+ * Parse a {@link ServerName} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link ServerName} object.
+ * @throws IOException
+ */
+ public static ServerName Parse(Stream input)
+ {
+ byte name_type = TlsUtilities.ReadUint8(input);
+ object name;
+
+ switch (name_type)
+ {
+ case Tls.NameType.host_name:
+ {
+ byte[] utf8Encoding = TlsUtilities.ReadOpaque16(input);
+ if (utf8Encoding.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ name = Strings.FromUtf8ByteArray(utf8Encoding);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new ServerName(name_type, name);
+ }
+
+ protected static bool IsCorrectType(byte nameType, object name)
+ {
+ switch (nameType)
+ {
+ case Tls.NameType.host_name:
+ return name is string;
+ default:
+ throw new ArgumentException("unsupported value", "name");
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/ServerNameList.cs b/crypto/src/crypto/tls/ServerNameList.cs
new file mode 100644
index 000000000..13da79bf6
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerNameList.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class ServerNameList
+ {
+ protected readonly IList mServerNameList;
+
+ /**
+ * @param serverNameList an {@link IList} of {@link ServerName}.
+ */
+ public ServerNameList(IList serverNameList)
+ {
+ if (serverNameList == null || serverNameList.Count < 1)
+ throw new ArgumentException("must not be null or empty", "serverNameList");
+
+ this.mServerNameList = serverNameList;
+ }
+
+ /**
+ * @return an {@link IList} of {@link ServerName}.
+ */
+ public virtual IList ServerNames
+ {
+ get { return mServerNameList; }
+ }
+
+ /**
+ * Encode this {@link ServerNameList} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ foreach (ServerName entry in ServerNames)
+ {
+ entry.Encode(buf);
+ }
+
+ TlsUtilities.CheckUint16(buf.Length);
+ TlsUtilities.WriteUint16((int)buf.Length, output);
+ buf.WriteTo(output);
+ }
+
+ /**
+ * Parse a {@link ServerNameList} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link ServerNameList} object.
+ * @throws IOException
+ */
+ public static ServerNameList Parse(Stream input)
+ {
+ int length = TlsUtilities.ReadUint16(input);
+ if (length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] data = TlsUtilities.ReadFully(length, input);
+
+ MemoryStream buf = new MemoryStream(data, false);
+
+ IList server_name_list = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ ServerName entry = ServerName.Parse(buf);
+ server_name_list.Add(entry);
+ }
+
+ return new ServerNameList(server_name_list);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs
new file mode 100644
index 000000000..485889709
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class ServerOnlyTlsAuthentication
+ : TlsAuthentication
+ {
+ public abstract void NotifyServerCertificate(Certificate serverCertificate);
+
+ public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ return null;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index 3769fc85d..5d82ed470 100644
--- a/crypto/src/crypto/tls/SrpTlsClient.cs
+++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -7,16 +7,10 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class SrpTlsClient
- : TlsClient
+ : AbstractTlsClient
{
- protected TlsCipherFactory cipherFactory;
- protected byte[] identity;
- protected byte[] password;
-
- protected TlsClientContext context;
-
- protected byte selectedCompressionMethod;
- protected int selectedCipherSuite;
+ protected byte[] mIdentity;
+ protected byte[] mPassword;
public SrpTlsClient(byte[] identity, byte[] password)
: this(new DefaultTlsCipherFactory(), identity, password)
@@ -24,176 +18,106 @@ namespace Org.BouncyCastle.Crypto.Tls
}
public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+ : base(cipherFactory)
{
- this.cipherFactory = cipherFactory;
- this.identity = Arrays.Clone(identity);
- this.password = Arrays.Clone(password);
- }
-
- public virtual void Init(TlsClientContext context)
- {
- this.context = context;
+ this.mIdentity = Arrays.Clone(identity);
+ this.mPassword = Arrays.Clone(password);
}
- public virtual bool ShouldUseGmtUnixTime()
+ protected virtual bool RequireSrpServerExtension
{
- /*
- * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
- * TLS implementors MUST by default set the entire value the ClientHello.Random and
- * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
- * sequence.
- */
- return false;
+ // No explicit guidance in RFC 5054; by default an (empty) extension from server is optional
+ get { return false; }
}
- public virtual int[] GetCipherSuites()
+ public override int[] GetCipherSuites()
{
- return new int[] {
- CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
+ return new int[]
+ {
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
};
}
- public virtual IDictionary GetClientExtensions()
+ public override IDictionary GetClientExtensions()
{
- IDictionary clientExtensions = Platform.CreateHashtable();
-
- MemoryStream srpData = new MemoryStream();
- TlsUtilities.WriteOpaque8(this.identity, srpData);
- clientExtensions[ExtensionType.srp] = srpData.ToArray();
-
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+ TlsSrpUtilities.AddSrpExtension(clientExtensions, this.mIdentity);
return clientExtensions;
}
- public virtual byte[] GetCompressionMethods()
- {
- return new byte[] { CompressionMethod.NULL };
- }
-
- public virtual void NotifySessionID(byte[] sessionID)
- {
- // Currently ignored
- }
-
- public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
- {
- this.selectedCipherSuite = selectedCipherSuite;
- }
-
- public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
+ public override void ProcessServerExtensions(IDictionary serverExtensions)
{
- this.selectedCompressionMethod = selectedCompressionMethod;
- }
-
- public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
- {
- if (!secureRenegotiation)
+ if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp,
+ AlertDescription.illegal_parameter))
{
- /*
- * RFC 5746 3.4. If the extension is not present, the server does not support
- * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
- * some clients may want to terminate the handshake instead of continuing; see
- * Section 4.1 for discussion.
- */
-// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ if (RequireSrpServerExtension)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- }
-
- public virtual void ProcessServerExtensions(IDictionary serverExtensions)
- {
- // There is no server response for the SRP extension
- }
- public virtual TlsKeyExchange GetKeyExchange()
- {
- switch (selectedCipherSuite)
- {
- case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
- return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
-
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
- return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
-
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
- return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
+ base.ProcessServerExtensions(serverExtensions);
}
-
- public abstract TlsAuthentication GetAuthentication();
- public virtual TlsCompression GetCompression()
+ public override TlsKeyExchange GetKeyExchange()
{
- switch (selectedCompressionMethod)
+ switch (mSelectedCipherSuite)
{
- case CompressionMethod.NULL:
- return new TlsNullCompression();
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected compression method was in the list of client-offered compression
- * methods, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
+
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
- public virtual TlsCipher GetCipher()
+ public override TlsCipher GetCipher()
{
- switch (selectedCipherSuite)
+ switch (mSelectedCipherSuite)
{
- case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
-
- case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange)
{
- return new TlsSrpKeyExchange(context, keyExchange, identity, password);
+ return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mIdentity, mPassword);
}
}
}
diff --git a/crypto/src/crypto/tls/Ssl3Mac.cs b/crypto/src/crypto/tls/Ssl3Mac.cs
index b2f3f309e..8bdb342dc 100644
--- a/crypto/src/crypto/tls/Ssl3Mac.cs
+++ b/crypto/src/crypto/tls/Ssl3Mac.cs
@@ -6,109 +6,105 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- /**
- * HMAC implementation based on original internet draft for HMAC (RFC 2104)
- *
- * The difference is that padding is concatentated versus XORed with the key
- *
- * H(K + opad, H(K + ipad, text))
- */
- public class Ssl3Mac
- : IMac
- {
- private const byte IPAD = 0x36;
- private const byte OPAD = 0x5C;
-
- internal static readonly byte[] MD5_IPAD = GenPad(IPAD, 48);
- internal static readonly byte[] MD5_OPAD = GenPad(OPAD, 48);
- internal static readonly byte[] SHA1_IPAD = GenPad(IPAD, 40);
- internal static readonly byte[] SHA1_OPAD = GenPad(OPAD, 40);
-
- private IDigest digest;
-
- private byte[] secret;
- private byte[] ipad, opad;
-
- /**
- * Base constructor for one of the standard digest algorithms that the byteLength of
- * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
- *
- * @param digest the digest.
- */
- public Ssl3Mac(IDigest digest)
- {
- this.digest = digest;
-
- if (digest.GetDigestSize() == 20)
- {
- this.ipad = SHA1_IPAD;
- this.opad = SHA1_OPAD;
- }
- else
- {
- this.ipad = MD5_IPAD;
- this.opad = MD5_OPAD;
- }
- }
-
- public virtual string AlgorithmName
- {
- get { return digest.AlgorithmName + "/SSL3MAC"; }
- }
-
- public virtual void Init(ICipherParameters parameters)
- {
- secret = Arrays.Clone(((KeyParameter)parameters).GetKey());
-
- Reset();
- }
-
- public virtual int GetMacSize()
- {
- return digest.GetDigestSize();
- }
-
- public virtual void Update(byte input)
- {
- digest.Update(input);
- }
-
- public virtual void BlockUpdate(byte[] input, int inOff, int len)
- {
- digest.BlockUpdate(input, inOff, len);
- }
-
- public virtual int DoFinal(byte[] output, int outOff)
- {
- byte[] tmp = new byte[digest.GetDigestSize()];
- digest.DoFinal(tmp, 0);
-
- digest.BlockUpdate(secret, 0, secret.Length);
- digest.BlockUpdate(opad, 0, opad.Length);
- digest.BlockUpdate(tmp, 0, tmp.Length);
-
- int len = digest.DoFinal(output, outOff);
-
- Reset();
-
- return len;
- }
-
- /**
- * Reset the mac generator.
- */
- public virtual void Reset()
- {
- digest.Reset();
- digest.BlockUpdate(secret, 0, secret.Length);
- digest.BlockUpdate(ipad, 0, ipad.Length);
- }
-
- private static byte[] GenPad(byte b, int count)
- {
- byte[] padding = new byte[count];
- Arrays.Fill(padding, b);
- return padding;
- }
- }
+ /**
+ * HMAC implementation based on original internet draft for HMAC (RFC 2104)
+ *
+ * The difference is that padding is concatentated versus XORed with the key
+ *
+ * H(K + opad, H(K + ipad, text))
+ */
+ public class Ssl3Mac
+ : IMac
+ {
+ private const byte IPAD_BYTE = 0x36;
+ private const byte OPAD_BYTE = 0x5C;
+
+ internal static readonly byte[] IPAD = GenPad(IPAD_BYTE, 48);
+ internal static readonly byte[] OPAD = GenPad(OPAD_BYTE, 48);
+
+ private readonly IDigest digest;
+ private readonly int padLength;
+
+ private byte[] secret;
+
+ /**
+ * Base constructor for one of the standard digest algorithms that the byteLength of
+ * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
+ *
+ * @param digest the digest.
+ */
+ public Ssl3Mac(IDigest digest)
+ {
+ this.digest = digest;
+
+ if (digest.GetDigestSize() == 20)
+ {
+ this.padLength = 40;
+ }
+ else
+ {
+ this.padLength = 48;
+ }
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "/SSL3MAC"; }
+ }
+
+ public virtual void Init(ICipherParameters parameters)
+ {
+ secret = Arrays.Clone(((KeyParameter)parameters).GetKey());
+
+ Reset();
+ }
+
+ public virtual int GetMacSize()
+ {
+ return digest.GetDigestSize();
+ }
+
+ public virtual void Update(byte input)
+ {
+ digest.Update(input);
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ digest.BlockUpdate(input, inOff, len);
+ }
+
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ byte[] tmp = new byte[digest.GetDigestSize()];
+ digest.DoFinal(tmp, 0);
+
+ digest.BlockUpdate(secret, 0, secret.Length);
+ digest.BlockUpdate(OPAD, 0, padLength);
+ digest.BlockUpdate(tmp, 0, tmp.Length);
+
+ int len = digest.DoFinal(output, outOff);
+
+ Reset();
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public virtual void Reset()
+ {
+ digest.Reset();
+ digest.BlockUpdate(secret, 0, secret.Length);
+ digest.BlockUpdate(IPAD, 0, padLength);
+ }
+
+ private static byte[] GenPad(byte b, int count)
+ {
+ byte[] padding = new byte[count];
+ Arrays.Fill(padding, b);
+ return padding;
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/SupplementalDataEntry.cs b/crypto/src/crypto/tls/SupplementalDataEntry.cs
new file mode 100644
index 000000000..5adc4fa52
--- /dev/null
+++ b/crypto/src/crypto/tls/SupplementalDataEntry.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class SupplementalDataEntry
+ {
+ protected readonly int mDataType;
+ protected readonly byte[] mData;
+
+ public SupplementalDataEntry(int dataType, byte[] data)
+ {
+ this.mDataType = dataType;
+ this.mData = data;
+ }
+
+ public virtual int DataType
+ {
+ get { return mDataType; }
+ }
+
+ public virtual byte[] Data
+ {
+ get { return mData; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsAeadCipher.cs b/crypto/src/crypto/tls/TlsAeadCipher.cs
new file mode 100644
index 000000000..951e8663b
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsAeadCipher.cs
@@ -0,0 +1,189 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsAeadCipher
+ : TlsCipher
+ {
+ protected readonly TlsContext context;
+ protected readonly int macSize;
+ protected readonly int nonce_explicit_length;
+
+ protected readonly IAeadBlockCipher encryptCipher;
+ protected readonly IAeadBlockCipher decryptCipher;
+
+ protected readonly byte[] encryptImplicitNonce, decryptImplicitNonce;
+
+ /// <exception cref="IOException"></exception>
+ public TlsAeadCipher(TlsContext context, IAeadBlockCipher clientWriteCipher, IAeadBlockCipher serverWriteCipher,
+ int cipherKeySize, int macSize)
+ {
+ if (!TlsUtilities.IsTlsV12(context))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.context = context;
+ this.macSize = macSize;
+
+ // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers
+ this.nonce_explicit_length = 8;
+
+ // TODO SecurityParameters.fixed_iv_length
+ int fixed_iv_length = 4;
+
+ int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length);
+
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ byte[] client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length);
+ offset += fixed_iv_length;
+ byte[] server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length);
+ offset += fixed_iv_length;
+
+ if (offset != key_block_size)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ KeyParameter encryptKey, decryptKey;
+ if (context.IsServer)
+ {
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ this.encryptImplicitNonce = server_write_IV;
+ this.decryptImplicitNonce = client_write_IV;
+ encryptKey = server_write_key;
+ decryptKey = client_write_key;
+ }
+ else
+ {
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ this.encryptImplicitNonce = client_write_IV;
+ this.decryptImplicitNonce = server_write_IV;
+ encryptKey = client_write_key;
+ decryptKey = server_write_key;
+ }
+
+ byte[] dummyNonce = new byte[fixed_iv_length + nonce_explicit_length];
+
+ this.encryptCipher.Init(true, new AeadParameters(encryptKey, 8 * macSize, dummyNonce));
+ this.decryptCipher.Init(false, new AeadParameters(decryptKey, 8 * macSize, dummyNonce));
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ // TODO We ought to be able to ask the decryptCipher (independently of it's current state!)
+ return ciphertextLimit - macSize - nonce_explicit_length;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
+ {
+ byte[] nonce = new byte[this.encryptImplicitNonce.Length + nonce_explicit_length];
+ Array.Copy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.Length);
+
+ /*
+ * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number.
+ *
+ * (May need review for other AEAD ciphers).
+ */
+ TlsUtilities.WriteUint64(seqNo, nonce, encryptImplicitNonce.Length);
+
+ int plaintextOffset = offset;
+ int plaintextLength = len;
+ int ciphertextLength = encryptCipher.GetOutputSize(plaintextLength);
+
+ byte[] output = new byte[nonce_explicit_length + ciphertextLength];
+ Array.Copy(nonce, encryptImplicitNonce.Length, output, 0, nonce_explicit_length);
+ int outputPos = nonce_explicit_length;
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
+ AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData);
+
+ try
+ {
+ encryptCipher.Init(true, parameters);
+ outputPos += encryptCipher.ProcessBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
+ outputPos += encryptCipher.DoFinal(output, outputPos);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+
+ if (outputPos != output.Length)
+ {
+ // NOTE: Existing AEAD cipher implementations all give exact output lengths
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return output;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
+ {
+ if (GetPlaintextLimit(len) < 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] nonce = new byte[this.decryptImplicitNonce.Length + nonce_explicit_length];
+ Array.Copy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.Length);
+ Array.Copy(ciphertext, offset, nonce, decryptImplicitNonce.Length, nonce_explicit_length);
+
+ int ciphertextOffset = offset + nonce_explicit_length;
+ int ciphertextLength = len - nonce_explicit_length;
+ int plaintextLength = decryptCipher.GetOutputSize(ciphertextLength);
+
+ byte[] output = new byte[plaintextLength];
+ int outputPos = 0;
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
+ AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData);
+
+ try
+ {
+ decryptCipher.Init(false, parameters);
+ outputPos += decryptCipher.ProcessBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
+ outputPos += decryptCipher.DoFinal(output, outputPos);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
+ }
+
+ if (outputPos != output.Length)
+ {
+ // NOTE: Existing AEAD cipher implementations all give exact output lengths
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return output;
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len)
+ {
+ /*
+ * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
+ * TLSCompressed.length
+ */
+
+ byte[] additional_data = new byte[13];
+ TlsUtilities.WriteUint64(seqNo, additional_data, 0);
+ TlsUtilities.WriteUint8(type, additional_data, 8);
+ TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9);
+ TlsUtilities.WriteUint16(len, additional_data, 11);
+
+ return additional_data;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsAgreementCredentials.cs b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
index 46ee4f90e..7c64072e8 100644
--- a/crypto/src/crypto/tls/TlsAgreementCredentials.cs
+++ b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -3,9 +3,10 @@ using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- public interface TlsAgreementCredentials : TlsCredentials
- {
- /// <exception cref="IOException"></exception>
- byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey);
- }
+ public interface TlsAgreementCredentials
+ : TlsCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey);
+ }
}
diff --git a/crypto/src/crypto/tls/TlsBlockCipher.cs b/crypto/src/crypto/tls/TlsBlockCipher.cs
index cfbceb25e..82c0318b2 100644
--- a/crypto/src/crypto/tls/TlsBlockCipher.cs
+++ b/crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -1,8 +1,6 @@
using System;
using System.IO;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
@@ -10,169 +8,302 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
/// <summary>
- /// A generic TLS 1.0 block cipher. This can be used for AES or 3DES for example.
+ /// A generic TLS 1.0-1.2 / SSLv3 block cipher. This can be used for AES or 3DES for example.
/// </summary>
public class TlsBlockCipher
- : TlsCipher
+ : TlsCipher
{
- protected TlsClientContext context;
- protected byte[] randomData;
+ protected readonly TlsContext context;
+ protected readonly byte[] randomData;
+ protected readonly bool useExplicitIV;
+ protected readonly bool encryptThenMac;
- protected IBlockCipher encryptCipher;
- protected IBlockCipher decryptCipher;
+ protected readonly IBlockCipher encryptCipher;
+ protected readonly IBlockCipher decryptCipher;
- protected TlsMac wMac;
- protected TlsMac rMac;
+ protected readonly TlsMac mWriteMac;
+ protected readonly TlsMac mReadMac;
public virtual TlsMac WriteMac
{
- get { return wMac; }
+ get { return mWriteMac; }
}
public virtual TlsMac ReadMac
{
- get { return rMac; }
+ get { return mReadMac; }
}
- public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher,
- IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
+ /// <exception cref="IOException"></exception>
+ public TlsBlockCipher(TlsContext context, IBlockCipher clientWriteCipher, IBlockCipher serverWriteCipher,
+ IDigest clientWriteDigest, IDigest serverWriteDigest, int cipherKeySize)
{
this.context = context;
this.randomData = new byte[256];
- context.SecureRandom.NextBytes(randomData);
+ context.NonceRandomGenerator.NextBytes(randomData);
- this.encryptCipher = encryptCipher;
- this.decryptCipher = decryptCipher;
+ this.useExplicitIV = TlsUtilities.IsTlsV11(context);
+ this.encryptThenMac = context.SecurityParameters.encryptThenMac;
- int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize()
- + readDigest.GetDigestSize() + encryptCipher.GetBlockSize()
- + decryptCipher.GetBlockSize();
+ int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize()
+ + serverWriteDigest.GetDigestSize();
- SecurityParameters securityParameters = context.SecurityParameters;
+ // From TLS 1.1 onwards, block ciphers don't need client_write_IV
+ if (!useExplicitIV)
+ {
+ key_block_size += clientWriteCipher.GetBlockSize() + serverWriteCipher.GetBlockSize();
+ }
- byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion",
- TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
- prfSize);
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
int offset = 0;
- // Init MACs
- wMac = CreateTlsMac(writeDigest, keyBlock, ref offset);
- rMac = CreateTlsMac(readDigest, keyBlock, ref offset);
+ TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.GetDigestSize());
+ offset += clientWriteDigest.GetDigestSize();
+ TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.GetDigestSize());
+ offset += serverWriteDigest.GetDigestSize();
- // Build keys
- KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
- KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+ KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
- // Add IVs
- ParametersWithIV encryptParams = CreateParametersWithIV(encryptKey,
- keyBlock, ref offset, encryptCipher.GetBlockSize());
- ParametersWithIV decryptParams = CreateParametersWithIV(decryptKey,
- keyBlock, ref offset, decryptCipher.GetBlockSize());
+ byte[] client_write_IV, server_write_IV;
+ if (useExplicitIV)
+ {
+ client_write_IV = new byte[clientWriteCipher.GetBlockSize()];
+ server_write_IV = new byte[serverWriteCipher.GetBlockSize()];
+ }
+ else
+ {
+ client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + clientWriteCipher.GetBlockSize());
+ offset += clientWriteCipher.GetBlockSize();
+ server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + serverWriteCipher.GetBlockSize());
+ offset += serverWriteCipher.GetBlockSize();
+ }
- if (offset != prfSize)
+ if (offset != key_block_size)
+ {
throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
- // Init Ciphers
- encryptCipher.Init(true, encryptParams);
- decryptCipher.Init(false, decryptParams);
- }
+ ICipherParameters encryptParams, decryptParams;
+ if (context.IsServer)
+ {
+ this.mWriteMac = serverWriteMac;
+ this.mReadMac = clientWriteMac;
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ encryptParams = new ParametersWithIV(server_write_key, server_write_IV);
+ decryptParams = new ParametersWithIV(client_write_key, client_write_IV);
+ }
+ else
+ {
+ this.mWriteMac = clientWriteMac;
+ this.mReadMac = serverWriteMac;
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ encryptParams = new ParametersWithIV(client_write_key, client_write_IV);
+ decryptParams = new ParametersWithIV(server_write_key, server_write_IV);
+ }
- protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off)
- {
- int len = digest.GetDigestSize();
- TlsMac mac = new TlsMac(digest, buf, off, len);
- off += len;
- return mac;
+ this.encryptCipher.Init(true, encryptParams);
+ this.decryptCipher.Init(false, decryptParams);
}
- protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len)
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
{
- KeyParameter key = new KeyParameter(buf, off, len);
- off += len;
- return key;
- }
+ int blockSize = encryptCipher.GetBlockSize();
+ int macSize = mWriteMac.Size;
- protected virtual ParametersWithIV CreateParametersWithIV(KeyParameter key,
- byte[] buf, ref int off, int len)
- {
- ParametersWithIV ivParams = new ParametersWithIV(key, buf, off, len);
- off += len;
- return ivParams;
+ int plaintextLimit = ciphertextLimit;
+
+ // An explicit IV consumes 1 block
+ if (useExplicitIV)
+ {
+ plaintextLimit -= blockSize;
+ }
+
+ // Leave room for the MAC, and require block-alignment
+ if (encryptThenMac)
+ {
+ plaintextLimit -= macSize;
+ plaintextLimit -= plaintextLimit % blockSize;
+ }
+ else
+ {
+ plaintextLimit -= plaintextLimit % blockSize;
+ plaintextLimit -= macSize;
+ }
+
+ // Minimum 1 byte of padding
+ --plaintextLimit;
+
+ return plaintextLimit;
}
- public virtual byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len)
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
{
- int blocksize = encryptCipher.GetBlockSize();
- int padding_length = blocksize - 1 - ((len + wMac.Size) % blocksize);
+ int blockSize = encryptCipher.GetBlockSize();
+ int macSize = mWriteMac.Size;
+
+ ProtocolVersion version = context.ServerVersion;
+
+ int enc_input_length = len;
+ if (!encryptThenMac)
+ {
+ enc_input_length += macSize;
+ }
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
+ int padding_length = blockSize - 1 - (enc_input_length % blockSize);
- if (isTls)
+ // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
+ if (!version.IsDtls && !version.IsSsl)
{
// Add a random number of extra blocks worth of padding
- int maxExtraPadBlocks = (255 - padding_length) / blocksize;
+ int maxExtraPadBlocks = (255 - padding_length) / blockSize;
int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks);
- padding_length += actualExtraPadBlocks * blocksize;
+ padding_length += actualExtraPadBlocks * blockSize;
+ }
+
+ int totalSize = len + macSize + padding_length + 1;
+ if (useExplicitIV)
+ {
+ totalSize += blockSize;
+ }
+
+ byte[] outBuf = new byte[totalSize];
+ int outOff = 0;
+
+ if (useExplicitIV)
+ {
+ byte[] explicitIV = new byte[blockSize];
+ context.NonceRandomGenerator.NextBytes(explicitIV);
+
+ encryptCipher.Init(true, new ParametersWithIV(null, explicitIV));
+
+ Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
+ outOff += blockSize;
+ }
+
+ int blocks_start = outOff;
+
+ Array.Copy(plaintext, offset, outBuf, outOff, len);
+ outOff += len;
+
+ if (!encryptThenMac)
+ {
+ byte[] mac = mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len);
+ Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+ outOff += mac.Length;
}
- int totalsize = len + wMac.Size + padding_length + 1;
- byte[] outbuf = new byte[totalsize];
- Array.Copy(plaintext, offset, outbuf, 0, len);
- byte[] mac = wMac.CalculateMac(type, plaintext, offset, len);
- Array.Copy(mac, 0, outbuf, len, mac.Length);
- int paddoffset = len + mac.Length;
for (int i = 0; i <= padding_length; i++)
{
- outbuf[i + paddoffset] = (byte)padding_length;
+ outBuf[outOff++] = (byte)padding_length;
}
- for (int i = 0; i < totalsize; i += blocksize)
+
+ for (int i = blocks_start; i < outOff; i += blockSize)
+ {
+ encryptCipher.ProcessBlock(outBuf, i, outBuf, i);
+ }
+
+ if (encryptThenMac)
{
- encryptCipher.ProcessBlock(outbuf, i, outbuf, i);
+ byte[] mac = mWriteMac.CalculateMac(seqNo, type, outBuf, 0, outOff);
+ Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+ outOff += mac.Length;
}
- return outbuf;
+
+ // assert outBuf.length == outOff;
+
+ return outBuf;
}
- public virtual byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len)
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
{
int blockSize = decryptCipher.GetBlockSize();
- int macSize = rMac.Size;
+ int macSize = mReadMac.Size;
+
+ int minLen = blockSize;
+ if (encryptThenMac)
+ {
+ minLen += macSize;
+ }
+ else
+ {
+ minLen = System.Math.Max(minLen, macSize + 1);
+ }
- /*
- * TODO[TLS 1.1] Explicit IV implies minLen = blockSize + max(blockSize, macSize + 1),
- * and will need further changes to offset and plen variables below.
- */
+ if (useExplicitIV)
+ {
+ minLen += blockSize;
+ }
- int minLen = System.Math.Max(blockSize, macSize + 1);
if (len < minLen)
throw new TlsFatalAlert(AlertDescription.decode_error);
- if (len % blockSize != 0)
+ int blocks_length = len;
+ if (encryptThenMac)
+ {
+ blocks_length -= macSize;
+ }
+
+ if (blocks_length % blockSize != 0)
throw new TlsFatalAlert(AlertDescription.decryption_failed);
- for (int i = 0; i < len; i += blockSize)
+ if (encryptThenMac)
{
- decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i);
+ int end = offset + len;
+ byte[] receivedMac = Arrays.CopyOfRange(ciphertext, end - macSize, end);
+ byte[] calculatedMac = mReadMac.CalculateMac(seqNo, type, ciphertext, offset, len - macSize);
+
+ bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac);
+
+ if (badMac)
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ if (useExplicitIV)
+ {
+ decryptCipher.Init(false, new ParametersWithIV(null, ciphertext, offset, blockSize));
+
+ offset += blockSize;
+ blocks_length -= blockSize;
}
- int plen = len;
+ for (int i = 0; i < blocks_length; i += blockSize)
+ {
+ decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i);
+ }
// If there's anything wrong with the padding, this will return zero
- int totalPad = CheckPaddingConstantTime(ciphertext, offset, plen, blockSize, macSize);
+ int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMac ? 0 : macSize);
- int macInputLen = plen - totalPad - macSize;
+ int dec_output_length = blocks_length - totalPad;
- byte[] decryptedMac = Arrays.Copy(ciphertext, offset + macInputLen, macSize);
- byte[] calculatedMac = rMac.CalculateMacConstantTime(type, ciphertext, offset, macInputLen, plen - macSize, randomData);
+ if (!encryptThenMac)
+ {
+ dec_output_length -= macSize;
+ int macInputLen = dec_output_length;
+ int macOff = offset + macInputLen;
+ byte[] receivedMac = Arrays.CopyOfRange(ciphertext, macOff, macOff + macSize);
+ byte[] calculatedMac = mReadMac.CalculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen,
+ blocks_length - macSize, randomData);
- bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac);
+ bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac);
- if (badMac || totalPad == 0)
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ if (badMac || totalPad == 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+ }
- return Arrays.Copy(ciphertext, offset, macInputLen);
+ return Arrays.CopyOfRange(ciphertext, offset, offset + dec_output_length);
}
protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
@@ -185,10 +316,7 @@ namespace Org.BouncyCastle.Crypto.Tls
int dummyIndex = 0;
byte padDiff = 0;
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
-
- if ((!isTls && totalPad > blockSize) || (macSize + totalPad > len))
+ if ((TlsUtilities.IsSsl(context) && totalPad > blockSize) || (macSize + totalPad > len))
{
totalPad = 0;
}
@@ -225,25 +353,24 @@ namespace Org.BouncyCastle.Crypto.Tls
protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max)
{
-// return r.NextInt(max + 1);
+ // return r.NextInt(max + 1);
- uint x = (uint)r.NextInt();
+ int x = r.NextInt();
int n = LowestBitSet(x);
return System.Math.Min(n, max);
}
- private int LowestBitSet(uint x)
+ protected virtual int LowestBitSet(int x)
{
if (x == 0)
- {
return 32;
- }
+ uint ux = (uint)x;
int n = 0;
- while ((x & 1) == 0)
+ while ((ux & 1U) == 0)
{
++n;
- x >>= 1;
+ ux >>= 1;
}
return n;
}
diff --git a/crypto/src/crypto/tls/TlsCipher.cs b/crypto/src/crypto/tls/TlsCipher.cs
index a58f4943f..7bd8573ac 100644
--- a/crypto/src/crypto/tls/TlsCipher.cs
+++ b/crypto/src/crypto/tls/TlsCipher.cs
@@ -5,10 +5,12 @@ namespace Org.BouncyCastle.Crypto.Tls
{
public interface TlsCipher
{
+ int GetPlaintextLimit(int ciphertextLimit);
+
/// <exception cref="IOException"></exception>
- byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len);
+ byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len);
/// <exception cref="IOException"></exception>
- byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len);
+ byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len);
}
}
diff --git a/crypto/src/crypto/tls/TlsCipherFactory.cs b/crypto/src/crypto/tls/TlsCipherFactory.cs
index bd65f8b4b..4e1fe0eb9 100644
--- a/crypto/src/crypto/tls/TlsCipherFactory.cs
+++ b/crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -6,7 +6,6 @@ namespace Org.BouncyCastle.Crypto.Tls
public interface TlsCipherFactory
{
/// <exception cref="IOException"></exception>
- TlsCipher CreateCipher(TlsClientContext context, int encryptionAlgorithm,
- DigestAlgorithm digestAlgorithm);
+ TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm);
}
}
diff --git a/crypto/src/crypto/tls/TlsClient.cs b/crypto/src/crypto/tls/TlsClient.cs
index a4cdc647d..cd5dfad13 100644
--- a/crypto/src/crypto/tls/TlsClient.cs
+++ b/crypto/src/crypto/tls/TlsClient.cs
@@ -15,6 +15,18 @@ namespace Org.BouncyCastle.Crypto.Tls
/// </param>
void Init(TlsClientContext context);
+ /// <summary>Return the session this client wants to resume, if any.</summary>
+ /// <remarks>Note that the peer's certificate chain for the session (if any) may need to be periodically revalidated.</remarks>
+ /// <returns>
+ /// A <see cref="TlsSession"/> representing the resumable session to be used for this connection,
+ /// or null to use a new session.
+ /// </returns>
+ TlsSession GetSessionToResume();
+
+ ProtocolVersion ClientHelloRecordLayerVersion { get; }
+
+ ProtocolVersion ClientVersion { get; }
+
/// <summary>
/// Get the list of cipher suites that this client supports.
/// </summary>
@@ -40,12 +52,13 @@ namespace Org.BouncyCastle.Crypto.Tls
/// <exception cref="IOException"></exception>
IDictionary GetClientExtensions();
+ /// <exception cref="IOException"></exception>
+ void NotifyServerVersion(ProtocolVersion selectedVersion);
+
/// <summary>
- /// Reports the session ID once it has been determined.
+ /// Notifies the client of the session_id sent in the ServerHello.
/// </summary>
- /// <param name="sessionID">
- /// A <see cref="System.Byte"/>
- /// </param>
+ /// <param name="sessionID">An array of <see cref="System.Byte"/></param>
void NotifySessionID(byte[] sessionID);
/// <summary>
@@ -73,18 +86,6 @@ namespace Org.BouncyCastle.Crypto.Tls
void NotifySelectedCompressionMethod(byte selectedCompressionMethod);
/// <summary>
- /// Report whether the server supports secure renegotiation
- /// </summary>
- /// <remarks>
- /// The protocol handler automatically processes the relevant extensions
- /// </remarks>
- /// <param name="secureRenegotiation">
- /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
- /// </param>
- /// <exception cref="IOException"></exception>
- void NotifySecureRenegotiation(bool secureRenegotiation);
-
- /// <summary>
/// Report the extensions from an extended server hello.
/// </summary>
/// <remarks>
@@ -95,6 +96,10 @@ namespace Org.BouncyCastle.Crypto.Tls
/// </param>
void ProcessServerExtensions(IDictionary serverExtensions);
+ /// <param name="serverSupplementalData">A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></param>
+ /// <exception cref="IOException"/>
+ void ProcessServerSupplementalData(IList serverSupplementalData);
+
/// <summary>
/// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange
/// part of the protocol.
@@ -112,19 +117,18 @@ namespace Org.BouncyCastle.Crypto.Tls
/// <exception cref="IOException"/>
TlsAuthentication GetAuthentication();
- /// <summary>
- /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
- /// </summary>
+ /// <returns>A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></returns>
/// <exception cref="IOException"/>
- TlsCompression GetCompression();
+ IList GetClientSupplementalData();
- /// <summary>
- /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
- /// </summary>
- /// <returns>
- /// A <see cref="TlsCipher"/>
- /// </returns>
+ /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message</summary>
+ /// <remarks>
+ /// This method will be called (only) when a NewSessionTicket handshake message is received. The
+ /// ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption
+ /// that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>.
+ /// </remarks>
+ /// <param name="newSessionTicket">The <see cref="NewSessionTicket">ticket</see></param>
/// <exception cref="IOException"/>
- TlsCipher GetCipher();
+ void NotifyNewSessionTicket(NewSessionTicket newSessionTicket);
}
}
diff --git a/crypto/src/crypto/tls/TlsClientContext.cs b/crypto/src/crypto/tls/TlsClientContext.cs
index dbb10aa76..b077d0aaf 100644
--- a/crypto/src/crypto/tls/TlsClientContext.cs
+++ b/crypto/src/crypto/tls/TlsClientContext.cs
@@ -4,12 +4,8 @@ using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Tls
{
- public interface TlsClientContext
- {
- SecureRandom SecureRandom { get; }
-
- SecurityParameters SecurityParameters { get; }
-
- object UserObject { get; set; }
- }
+ public interface TlsClientContext
+ : TlsContext
+ {
+ }
}
diff --git a/crypto/src/crypto/tls/TlsClientContextImpl.cs b/crypto/src/crypto/tls/TlsClientContextImpl.cs
index 9d5dee232..674d68937 100644
--- a/crypto/src/crypto/tls/TlsClientContextImpl.cs
+++ b/crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -4,34 +4,17 @@ using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Tls
{
- internal class TlsClientContextImpl
- : TlsClientContext
- {
- private readonly SecureRandom secureRandom;
- private readonly SecurityParameters securityParameters;
+ internal class TlsClientContextImpl
+ : AbstractTlsContext, TlsClientContext
+ {
+ internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+ : base(secureRandom, securityParameters)
+ {
+ }
- private object userObject = null;
-
- internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
- {
- this.secureRandom = secureRandom;
- this.securityParameters = securityParameters;
- }
-
- public virtual SecureRandom SecureRandom
- {
- get { return secureRandom; }
- }
-
- public virtual SecurityParameters SecurityParameters
- {
- get { return securityParameters; }
- }
-
- public virtual object UserObject
- {
- get { return userObject; }
- set { this.userObject = value; }
- }
- }
+ public override bool IsServer
+ {
+ get { return false; }
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs
new file mode 100644
index 000000000..9fe50add8
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -0,0 +1,885 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsClientProtocol
+ : TlsProtocol
+ {
+ protected TlsClient mTlsClient = null;
+ internal TlsClientContextImpl mTlsClientContext = null;
+
+ protected byte[] mSelectedSessionID = null;
+
+ protected TlsKeyExchange mKeyExchange = null;
+ protected TlsAuthentication mAuthentication = null;
+
+ protected CertificateStatus mCertificateStatus = null;
+ protected CertificateRequest mCertificateRequest = null;
+
+ public TlsClientProtocol(Stream stream, SecureRandom secureRandom)
+ : base(stream, secureRandom)
+ {
+ }
+
+ public TlsClientProtocol(Stream input, Stream output, SecureRandom secureRandom)
+ : base(input, output, secureRandom)
+ {
+ }
+
+ /**
+ * Initiates a TLS handshake in the role of client
+ *
+ * @param tlsClient The {@link TlsClient} to use for the handshake.
+ * @throws IOException If handshake was not successful.
+ */
+ public virtual void Connect(TlsClient tlsClient)
+ {
+ if (tlsClient == null)
+ throw new ArgumentNullException("tlsClient");
+ if (this.mTlsClient != null)
+ throw new InvalidOperationException("'Connect' can only be called once");
+
+ this.mTlsClient = tlsClient;
+
+ this.mSecurityParameters = new SecurityParameters();
+ this.mSecurityParameters.entity = ConnectionEnd.client;
+
+ this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters);
+
+ this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
+ mTlsClientContext.NonceRandomGenerator);
+
+ this.mTlsClient.Init(mTlsClientContext);
+ this.mRecordStream.Init(mTlsClientContext);
+
+ TlsSession sessionToResume = tlsClient.GetSessionToResume();
+ if (sessionToResume != null)
+ {
+ SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
+ if (sessionParameters != null)
+ {
+ this.mTlsSession = sessionToResume;
+ this.mSessionParameters = sessionParameters;
+ }
+ }
+
+ SendClientHelloMessage();
+ this.mConnectionState = CS_CLIENT_HELLO;
+
+ CompleteHandshake();
+ }
+
+ protected override void CleanupHandshake()
+ {
+ base.CleanupHandshake();
+
+ this.mSelectedSessionID = null;
+ this.mKeyExchange = null;
+ this.mAuthentication = null;
+ this.mCertificateStatus = null;
+ this.mCertificateRequest = null;
+ }
+
+ protected override TlsContext Context
+ {
+ get { return mTlsClientContext; }
+ }
+
+ internal override AbstractTlsContext ContextAdmin
+ {
+ get { return mTlsClientContext; }
+ }
+
+ protected override TlsPeer Peer
+ {
+ get { return mTlsClient; }
+ }
+
+ protected override void HandleHandshakeMessage(byte type, byte[] data)
+ {
+ MemoryStream buf = new MemoryStream(data, false);
+
+ if (this.mResumedSession)
+ {
+ if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ProcessFinishedMessage(buf);
+ this.mConnectionState = CS_SERVER_FINISHED;
+
+ SendFinishedMessage();
+ this.mConnectionState = CS_CLIENT_FINISHED;
+ this.mConnectionState = CS_END;
+
+ return;
+ }
+
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ {
+ if (this.mConnectionState == CS_SERVER_HELLO)
+ {
+ HandleSupplementalData(null);
+ }
+
+ // Parse the Certificate message and Send to cipher suite
+
+ this.mPeerCertificate = Certificate.Parse(buf);
+
+ AssertEmpty(buf);
+
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty)
+ {
+ this.mAllowCertificateStatus = false;
+ }
+
+ this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate);
+
+ this.mAuthentication = mTlsClient.GetAuthentication();
+ this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_SERVER_CERTIFICATE;
+ break;
+ }
+ case HandshakeType.certificate_status:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ {
+ if (!this.mAllowCertificateStatus)
+ {
+ /*
+ * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
+ * server MUST have included an extension of type "status_request" with empty
+ * "extension_data" in the extended server hello..
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mCertificateStatus = CertificateStatus.Parse(buf);
+
+ AssertEmpty(buf);
+
+ // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
+
+ this.mConnectionState = CS_CERTIFICATE_STATUS;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_FINISHED:
+ case CS_SERVER_SESSION_TICKET:
+ {
+ if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST be sent if the server included a
+ * SessionTicket extension in the ServerHello.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ ProcessFinishedMessage(buf);
+ this.mConnectionState = CS_SERVER_FINISHED;
+ this.mConnectionState = CS_END;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_HELLO:
+ {
+ ReceiveServerHelloMessage(buf);
+ this.mConnectionState = CS_SERVER_HELLO;
+
+ if (this.mSecurityParameters.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + this.mSecurityParameters.maxFragmentLength);
+ mRecordStream.SetPlaintextLimit(plainTextLimit);
+ }
+
+ this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context,
+ this.mSecurityParameters.CipherSuite);
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
+ * verify_data_length has a verify_data_length equal to 12. This includes all
+ * existing cipher suites.
+ */
+ this.mSecurityParameters.verifyDataLength = 12;
+
+ this.mRecordStream.NotifyHelloComplete();
+
+ if (this.mResumedSession)
+ {
+ this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret);
+ this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
+
+ SendChangeCipherSpecMessage();
+ }
+ else
+ {
+ InvalidateSession();
+
+ if (this.mSelectedSessionID.Length > 0)
+ {
+ this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null);
+ }
+ }
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.supplemental_data:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ {
+ HandleSupplementalData(ReadSupplementalDataMessage(buf));
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello_done:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ case CS_CERTIFICATE_REQUEST:
+ {
+ if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
+ {
+ HandleSupplementalData(null);
+ }
+
+ if (mConnectionState < CS_SERVER_CERTIFICATE)
+ {
+ // There was no server certificate message; check it's OK
+ this.mKeyExchange.SkipServerCredentials();
+ this.mAuthentication = null;
+ }
+
+ if (mConnectionState < CS_SERVER_KEY_EXCHANGE)
+ {
+ // There was no server key exchange message; check it's OK
+ this.mKeyExchange.SkipServerKeyExchange();
+ }
+
+ AssertEmpty(buf);
+
+ this.mConnectionState = CS_SERVER_HELLO_DONE;
+
+ this.mRecordStream.HandshakeHash.SealHashAlgorithms();
+
+ IList clientSupplementalData = mTlsClient.GetClientSupplementalData();
+ if (clientSupplementalData != null)
+ {
+ SendSupplementalDataMessage(clientSupplementalData);
+ }
+ this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
+
+ TlsCredentials clientCreds = null;
+ if (mCertificateRequest == null)
+ {
+ this.mKeyExchange.SkipClientCredentials();
+ }
+ else
+ {
+ clientCreds = this.mAuthentication.GetClientCredentials(mCertificateRequest);
+
+ if (clientCreds == null)
+ {
+ this.mKeyExchange.SkipClientCredentials();
+
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST Send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ SendCertificateMessage(Certificate.EmptyChain);
+ }
+ else
+ {
+ this.mKeyExchange.ProcessClientCredentials(clientCreds);
+
+ SendCertificateMessage(clientCreds.Certificate);
+ }
+ }
+
+ this.mConnectionState = CS_CLIENT_CERTIFICATE;
+
+ /*
+ * Send the client key exchange message, depending on the key exchange we are using
+ * in our CipherSuite.
+ */
+ SendClientKeyExchangeMessage();
+ this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;
+
+ TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish();
+ this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, prepareFinishHash, null);
+
+ EstablishMasterSecret(Context, mKeyExchange);
+ mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
+
+ if (clientCreds != null && clientCreds is TlsSignerCredentials)
+ {
+ TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;
+
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ byte[] hash;
+
+ if (TlsUtilities.IsTlsV12(Context))
+ {
+ signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm;
+ if (signatureAndHashAlgorithm == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ hash = mSecurityParameters.SessionHash;
+ }
+
+ byte[] signature = signerCredentials.GenerateCertificateSignature(hash);
+ DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ SendCertificateVerifyMessage(certificateVerify);
+
+ this.mConnectionState = CS_CERTIFICATE_VERIFY;
+ }
+
+ SendChangeCipherSpecMessage();
+ SendFinishedMessage();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ this.mConnectionState = CS_CLIENT_FINISHED;
+ break;
+ }
+ case HandshakeType.server_key_exchange:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_CERTIFICATE_STATUS:
+ {
+ if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
+ {
+ HandleSupplementalData(null);
+ }
+
+ if (mConnectionState < CS_SERVER_CERTIFICATE)
+ {
+ // There was no server certificate message; check it's OK
+ this.mKeyExchange.SkipServerCredentials();
+ this.mAuthentication = null;
+ }
+
+ this.mKeyExchange.ProcessServerKeyExchange(buf);
+
+ AssertEmpty(buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_SERVER_KEY_EXCHANGE;
+ break;
+ }
+ case HandshakeType.certificate_request:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ case CS_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ {
+ if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE)
+ {
+ // There was no server key exchange message; check it's OK
+ this.mKeyExchange.SkipServerKeyExchange();
+ }
+
+ if (this.mAuthentication == null)
+ {
+ /*
+ * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
+ * to request client identification.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ this.mCertificateRequest = CertificateRequest.Parse(Context, buf);
+
+ AssertEmpty(buf);
+
+ this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest);
+
+ /*
+ * TODO Give the client a chance to immediately select the CertificateVerify hash
+ * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+ */
+ TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
+ this.mCertificateRequest.SupportedSignatureAlgorithms);
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_CERTIFICATE_REQUEST;
+ break;
+ }
+ case HandshakeType.session_ticket:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_FINISHED:
+ {
+ if (!this.mExpectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
+ * SessionTicket extension in the ServerHello.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ /*
+ * RFC 5077 3.4. If the client receives a session ticket from the server, then it
+ * discards any Session ID that was sent in the ServerHello.
+ */
+ InvalidateSession();
+
+ ReceiveNewSessionTicketMessage(buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_SERVER_SESSION_TICKET;
+ break;
+ }
+ case HandshakeType.hello_request:
+ {
+ AssertEmpty(buf);
+
+ /*
+ * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the
+ * client is currently negotiating a session. This message may be ignored by the client
+ * if it does not wish to renegotiate a session, or the client may, if it wishes,
+ * respond with a no_renegotiation alert.
+ */
+ if (this.mConnectionState == CS_END)
+ {
+ /*
+ * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal
+ * handshake_failure alert.
+ */
+ if (TlsUtilities.IsSsl(Context))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ string message = "Renegotiation not supported";
+ RaiseWarning(AlertDescription.no_renegotiation, message);
+ }
+ break;
+ }
+ case HandshakeType.client_hello:
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.certificate_verify:
+ case HandshakeType.hello_verify_request:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ protected virtual void HandleSupplementalData(IList serverSupplementalData)
+ {
+ this.mTlsClient.ProcessServerSupplementalData(serverSupplementalData);
+ this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA;
+
+ this.mKeyExchange = mTlsClient.GetKeyExchange();
+ this.mKeyExchange.Init(Context);
+ }
+
+ protected virtual void ReceiveNewSessionTicketMessage(MemoryStream buf)
+ {
+ NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf);
+
+ AssertEmpty(buf);
+
+ mTlsClient.NotifyNewSessionTicket(newSessionTicket);
+ }
+
+ protected virtual void ReceiveServerHelloMessage(MemoryStream buf)
+ {
+ ProtocolVersion server_version = TlsUtilities.ReadVersion(buf);
+ if (server_version.IsDtls)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ // Check that this matches what the server is Sending in the record layer
+ if (!server_version.Equals(this.mRecordStream.ReadVersion))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ ProtocolVersion client_version = Context.ClientVersion;
+ if (!server_version.IsEqualOrEarlierVersionOf(client_version))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mRecordStream.SetWriteVersion(server_version);
+ ContextAdmin.SetServerVersion(server_version);
+ this.mTlsClient.NotifyServerVersion(server_version);
+
+ /*
+ * Read the server random
+ */
+ this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf);
+
+ this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf);
+ if (this.mSelectedSessionID.Length > 32)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mTlsClient.NotifySessionID(this.mSelectedSessionID);
+
+ this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null
+ && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID);
+
+ /*
+ * Find out which CipherSuite the server has chosen and check that it was one of the offered
+ * ones, and is a valid selection for the negotiated version.
+ */
+ int selectedCipherSuite = TlsUtilities.ReadUint16(buf);
+ if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite)
+ || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
+
+ /*
+ * Find out which CompressionMethod the server has chosen and check that it was one of the
+ * offered ones.
+ */
+ byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf);
+ if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
+
+ /*
+ * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
+ * hello message when the client has requested extended functionality via the extended
+ * client hello message specified in Section 2.1. ... Note that the extended server hello
+ * message is only sent in response to an extended client hello message. This prevents the
+ * possibility that the extended server hello message could "break" existing TLS 1.0
+ * clients.
+ */
+ this.mServerExtensions = ReadExtensions(buf);
+
+ /*
+ * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret"
+ * extension, it MUST include the "extended_master_secret" extension in its ServerHello
+ * message.
+ */
+ bool serverSentExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions);
+ if (serverSentExtendedMasterSecret != mSecurityParameters.extendedMasterSecret)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ /*
+ * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
+ * extended client hello message.
+ *
+ * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
+ * Hello is always allowed.
+ */
+ if (this.mServerExtensions != null)
+ {
+ foreach (int extType in this.mServerExtensions.Keys)
+ {
+ /*
+ * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a
+ * ClientHello containing only the SCSV is an explicit exception to the prohibition
+ * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is
+ * only allowed because the client is signaling its willingness to receive the
+ * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+ if (extType == ExtensionType.renegotiation_info)
+ continue;
+
+ /*
+ * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
+ * same extension type appeared in the corresponding ClientHello. If a client
+ * receives an extension type in ServerHello that it did not request in the
+ * associated ClientHello, it MUST abort the handshake with an unsupported_extension
+ * fatal alert.
+ */
+ if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType))
+ throw new TlsFatalAlert(AlertDescription.unsupported_extension);
+
+ /*
+ * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to
+ * proceed with resumption, the extension does not have any effect. Requiring the
+ * extension to be included anyway makes the extension negotiation logic easier,
+ * because it does not depend on whether resumption is accepted or not.
+ */
+ if (extType == ExtensionType.extended_master_secret)
+ continue;
+
+ /*
+ * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and Send a server hello containing no
+ * extensions[.]
+ */
+ if (this.mResumedSession)
+ {
+ // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats
+ // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats
+ // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats
+ // throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake
+ */
+ {
+ /*
+ * When a ServerHello is received, the client MUST check if it includes the
+ * "renegotiation_info" extension:
+ */
+ byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info);
+ if (renegExtData != null)
+ {
+ /*
+ * If the extension is present, set the secure_renegotiation flag to TRUE. The
+ * client MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal
+ * handshake_failure alert).
+ */
+ this.mSecureRenegotiation = true;
+
+ if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming
+ this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation);
+
+ IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions;
+ if (this.mResumedSession)
+ {
+ if (selectedCipherSuite != this.mSessionParameters.CipherSuite
+ || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ sessionClientExtensions = null;
+ sessionServerExtensions = this.mSessionParameters.ReadServerExtensions();
+
+ this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
+ }
+
+ this.mSecurityParameters.cipherSuite = selectedCipherSuite;
+ this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;
+
+ if (sessionServerExtensions != null)
+ {
+ /*
+ * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
+ * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
+ * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
+ * client.
+ */
+ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions);
+ if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC;
+
+ this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
+ sessionServerExtensions, AlertDescription.illegal_parameter);
+
+ this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions);
+
+ /*
+ * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+ * a session resumption handshake.
+ */
+ this.mAllowCertificateStatus = !this.mResumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request,
+ AlertDescription.illegal_parameter);
+
+ this.mExpectSessionTicket = !this.mResumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket,
+ AlertDescription.illegal_parameter);
+ }
+
+ if (sessionClientExtensions != null)
+ {
+ this.mTlsClient.ProcessServerExtensions(sessionServerExtensions);
+ }
+ }
+
+ protected virtual void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify);
+
+ certificateVerify.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendClientHelloMessage()
+ {
+ this.mRecordStream.SetWriteVersion(this.mTlsClient.ClientHelloRecordLayerVersion);
+
+ ProtocolVersion client_version = this.mTlsClient.ClientVersion;
+ if (client_version.IsDtls)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ ContextAdmin.SetClientVersion(client_version);
+
+ /*
+ * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a
+ * Session ID in the TLS ClientHello.
+ */
+ byte[] session_id = TlsUtilities.EmptyBytes;
+ if (this.mTlsSession != null)
+ {
+ session_id = this.mTlsSession.SessionID;
+ if (session_id == null || session_id.Length > 32)
+ {
+ session_id = TlsUtilities.EmptyBytes;
+ }
+ }
+
+ this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites();
+
+ this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods();
+
+ if (session_id.Length > 0 && this.mSessionParameters != null)
+ {
+ if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite)
+ || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm))
+ {
+ session_id = TlsUtilities.EmptyBytes;
+ }
+ }
+
+ this.mClientExtensions = this.mTlsClient.GetClientExtensions();
+
+ this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions);
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
+
+ TlsUtilities.WriteVersion(client_version, message);
+
+ message.Write(this.mSecurityParameters.ClientRandom);
+
+ TlsUtilities.WriteOpaque8(session_id, message);
+
+ // Cipher Suites (and SCSV)
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+ byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info);
+ bool noRenegExt = (null == renegExtData);
+
+ bool noSCSV = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+
+ if (noRenegExt && noSCSV)
+ {
+ // TODO Consider whether to default to a client extension instead
+ // this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions);
+ // this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
+ this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ }
+
+ TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message);
+ }
+
+ TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message);
+
+ if (mClientExtensions != null)
+ {
+ WriteExtensions(message, mClientExtensions);
+ }
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendClientKeyExchangeMessage()
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange);
+
+ this.mKeyExchange.GenerateClientKeyExchange(message);
+
+ message.WriteToRecordStream(this);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
index 26d76fd3d..b831249a6 100644
--- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -1,86 +1,98 @@
using System;
+using System.Collections;
using System.IO;
-using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <summary>
- /// TLS 1.0 DH key exchange.
- /// </summary>
- internal class TlsDHKeyExchange
- : TlsKeyExchange
+ /// <summary>(D)TLS DH key exchange.</summary>
+ public class TlsDHKeyExchange
+ : AbstractTlsKeyExchange
{
- protected TlsClientContext context;
- protected int keyExchange;
- protected TlsSigner tlsSigner;
+ protected TlsSigner mTlsSigner;
+ protected DHParameters mDHParameters;
- protected AsymmetricKeyParameter serverPublicKey = null;
- protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
- protected TlsAgreementCredentials agreementCredentials;
- protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+ protected AsymmetricKeyParameter mServerPublicKey;
+ protected DHPublicKeyParameters mDHAgreeServerPublicKey;
+ protected TlsAgreementCredentials mAgreementCredentials;
+ protected DHPrivateKeyParameters mDHAgreeClientPrivateKey;
- internal TlsDHKeyExchange(TlsClientContext context, int keyExchange)
+ protected DHPrivateKeyParameters mDHAgreeServerPrivateKey;
+ protected DHPublicKeyParameters mDHAgreeClientPublicKey;
+
+ public TlsDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters)
+ : base(keyExchange, supportedSignatureAlgorithms)
{
switch (keyExchange)
{
- case KeyExchangeAlgorithm.DH_RSA:
- case KeyExchangeAlgorithm.DH_DSS:
- this.tlsSigner = null;
- break;
- case KeyExchangeAlgorithm.DHE_RSA:
- this.tlsSigner = new TlsRsaSigner();
- break;
- case KeyExchangeAlgorithm.DHE_DSS:
- this.tlsSigner = new TlsDssSigner();
- break;
- default:
- throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DH_DSS:
+ this.mTlsSigner = null;
+ break;
+ case KeyExchangeAlgorithm.DHE_RSA:
+ this.mTlsSigner = new TlsRsaSigner();
+ break;
+ case KeyExchangeAlgorithm.DHE_DSS:
+ this.mTlsSigner = new TlsDssSigner();
+ break;
+ default:
+ throw new InvalidOperationException("unsupported key exchange algorithm");
}
- this.context = context;
- this.keyExchange = keyExchange;
+ this.mDHParameters = dhParameters;
+ }
+
+ public override void Init(TlsContext context)
+ {
+ base.Init(context);
+
+ if (this.mTlsSigner != null)
+ {
+ this.mTlsSigner.Init(context);
+ }
}
- public virtual void SkipServerCertificate()
+ public override void SkipServerCredentials()
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ public override void ProcessServerCertificate(Certificate serverCertificate)
{
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
- SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
try
{
- this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
}
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
- if (tlsSigner == null)
+ if (mTlsSigner == null)
{
try
{
- this.dhAgreeServerPublicKey = ValidateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+ this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey((DHPublicKeyParameters)this.mServerPublicKey);
}
- catch (InvalidCastException)
+ catch (InvalidCastException e)
{
- throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
}
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement);
}
else
{
- if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+ if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey))
{
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
}
@@ -88,55 +100,51 @@ namespace Org.BouncyCastle.Crypto.Tls
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
}
- // TODO
- /*
- * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
- * signing algorithm for the certificate must be the same as the algorithm for the
- * certificate key."
- */
- }
-
- public virtual void SkipServerKeyExchange()
- {
- // OK
+ base.ProcessServerCertificate(serverCertificate);
}
- public virtual void ProcessServerKeyExchange(Stream input)
+ public override bool RequiresServerKeyExchange
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ get
+ {
+ switch (mKeyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.DH_anon:
+ return true;
+ default:
+ return false;
+ }
+ }
}
- public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
{
byte[] types = certificateRequest.CertificateTypes;
- foreach (byte type in types)
+ for (int i = 0; i < types.Length; ++i)
{
- switch (type)
+ switch (types[i])
{
- case ClientCertificateType.rsa_sign:
- case ClientCertificateType.dss_sign:
- case ClientCertificateType.rsa_fixed_dh:
- case ClientCertificateType.dss_fixed_dh:
- case ClientCertificateType.ecdsa_sign:
- break;
- default:
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.rsa_fixed_dh:
+ case ClientCertificateType.dss_fixed_dh:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
}
- public virtual void SkipClientCredentials()
- {
- this.agreementCredentials = null;
- }
-
- public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
{
if (clientCredentials is TlsAgreementCredentials)
{
// TODO Validate client cert has matching parameters (see 'areCompatibleParameters')?
- this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials;
}
else if (clientCredentials is TlsSignerCredentials)
{
@@ -148,54 +156,38 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
- public virtual void GenerateClientKeyExchange(Stream output)
+ public override void GenerateClientKeyExchange(Stream output)
{
/*
- * RFC 2246 7.4.7.2 If the client certificate already contains a suitable
- * Diffie-Hellman key, then Yc is implicit and does not need to be sent again. In
- * this case, the Client Key Exchange message will be sent, but will be empty.
+ * RFC 2246 7.4.7.2 If the client certificate already contains a suitable Diffie-Hellman
+ * key, then Yc is implicit and does not need to be sent again. In this case, the Client Key
+ * Exchange message will be sent, but will be empty.
*/
- if (agreementCredentials == null)
+ if (mAgreementCredentials == null)
{
- GenerateEphemeralClientKeyExchange(dhAgreeServerPublicKey.Parameters, output);
+ this.mDHAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+ mDHAgreeServerPublicKey.Parameters, output);
}
}
- public virtual byte[] GeneratePremasterSecret()
+ public override byte[] GeneratePremasterSecret()
{
- if (agreementCredentials != null)
+ if (mAgreementCredentials != null)
{
- return agreementCredentials.GenerateAgreement(dhAgreeServerPublicKey);
+ return mAgreementCredentials.GenerateAgreement(mDHAgreeServerPublicKey);
}
- return CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
- }
-
- protected virtual bool AreCompatibleParameters(DHParameters a, DHParameters b)
- {
- return a.P.Equals(b.P) && a.G.Equals(b.G);
- }
-
- protected virtual byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
- DHPrivateKeyParameters privateKey)
- {
- return TlsDHUtilities.CalculateDHBasicAgreement(publicKey, privateKey);
- }
-
- protected virtual AsymmetricCipherKeyPair GenerateDHKeyPair(DHParameters dhParams)
- {
- return TlsDHUtilities.GenerateDHKeyPair(context.SecureRandom, dhParams);
- }
+ if (mDHAgreeServerPrivateKey != null)
+ {
+ return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeClientPublicKey, mDHAgreeServerPrivateKey);
+ }
- protected virtual void GenerateEphemeralClientKeyExchange(DHParameters dhParams, Stream output)
- {
- this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
- context.SecureRandom, dhParams, output);
- }
+ if (mDHAgreeClientPrivateKey != null)
+ {
+ return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeServerPublicKey, mDHAgreeClientPrivateKey);
+ }
- protected virtual DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key)
- {
- return TlsDHUtilities.ValidateDHPublicKey(key);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
}
diff --git a/crypto/src/crypto/tls/TlsDHUtilities.cs b/crypto/src/crypto/tls/TlsDHUtilities.cs
index b5deb8b84..b29f75e30 100644
--- a/crypto/src/crypto/tls/TlsDHUtilities.cs
+++ b/crypto/src/crypto/tls/TlsDHUtilities.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.IO;
using Org.BouncyCastle.Crypto.Agreement;
@@ -7,11 +8,391 @@ using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class TlsDHUtilities
{
+ internal static readonly BigInteger Two = BigInteger.Two;
+
+ /*
+ * TODO[draft-ietf-tls-negotiated-ff-dhe-01] Move these groups to DHStandardGroups once reaches RFC
+ */
+ private static BigInteger FromHex(String hex)
+ {
+ return new BigInteger(1, Hex.Decode(hex));
+ }
+
+ private static DHParameters FromSafeP(String hexP)
+ {
+ BigInteger p = FromHex(hexP), q = p.ShiftRight(1);
+ return new DHParameters(p, Two, q);
+ }
+
+ private static readonly string draft_ffdhe2432_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE13098533C8B3FFFFFFFFFFFFFFFF";
+ internal static readonly DHParameters draft_ffdhe2432 = FromSafeP(draft_ffdhe2432_p);
+
+ private static readonly string draft_ffdhe3072_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF";
+ internal static readonly DHParameters draft_ffdhe3072 = FromSafeP(draft_ffdhe3072_p);
+
+ private static readonly string draft_ffdhe4096_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB"
+ + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832"
+ + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF"
+ + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A"
+ + "FFFFFFFFFFFFFFFF";
+ internal static readonly DHParameters draft_ffdhe4096 = FromSafeP(draft_ffdhe4096_p);
+
+ private static readonly string draft_ffdhe6144_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB"
+ + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832"
+ + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF"
+ + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902"
+ + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6"
+ + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A"
+ + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477"
+ + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3"
+ + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4"
+ + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6"
+ + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C"
+ + "D72B03746AE77F5E62292C311562A846505DC82DB854338A"
+ + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04"
+ + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1"
+ + "A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF";
+ internal static readonly DHParameters draft_ffdhe6144 = FromSafeP(draft_ffdhe6144_p);
+
+ private static readonly string draft_ffdhe8192_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB"
+ + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832"
+ + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF"
+ + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902"
+ + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6"
+ + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A"
+ + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477"
+ + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3"
+ + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4"
+ + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6"
+ + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C"
+ + "D72B03746AE77F5E62292C311562A846505DC82DB854338A"
+ + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04"
+ + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1"
+ + "A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838"
+ + "1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E"
+ + "0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665"
+ + "CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282"
+ + "2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022"
+ + "BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C"
+ + "51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9"
+ + "D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457"
+ + "1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30"
+ + "FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D"
+ + "97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C"
+ + "D68C8BB7C5C6424CFFFFFFFFFFFFFFFF";
+ internal static readonly DHParameters draft_ffdhe8192 = FromSafeP(draft_ffdhe8192_p);
+
+
+ public static void AddNegotiatedDheGroupsClientExtension(IDictionary extensions, byte[] dheGroups)
+ {
+ extensions[ExtensionType.negotiated_ff_dhe_groups] = CreateNegotiatedDheGroupsClientExtension(dheGroups);
+ }
+
+ public static void AddNegotiatedDheGroupsServerExtension(IDictionary extensions, byte dheGroup)
+ {
+ extensions[ExtensionType.negotiated_ff_dhe_groups] = CreateNegotiatedDheGroupsServerExtension(dheGroup);
+ }
+
+ public static byte[] GetNegotiatedDheGroupsClientExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.negotiated_ff_dhe_groups);
+ return extensionData == null ? null : ReadNegotiatedDheGroupsClientExtension(extensionData);
+ }
+
+ public static short GetNegotiatedDheGroupsServerExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.negotiated_ff_dhe_groups);
+ return extensionData == null ? (short)-1 : (short)ReadNegotiatedDheGroupsServerExtension(extensionData);
+ }
+
+ public static byte[] CreateNegotiatedDheGroupsClientExtension(byte[] dheGroups)
+ {
+ if (dheGroups == null || dheGroups.Length < 1 || dheGroups.Length > 255)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeUint8ArrayWithUint8Length(dheGroups);
+ }
+
+ public static byte[] CreateNegotiatedDheGroupsServerExtension(byte dheGroup)
+ {
+ return new byte[]{ dheGroup };
+ }
+
+ public static byte[] ReadNegotiatedDheGroupsClientExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ byte length = TlsUtilities.ReadUint8(buf);
+ if (length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] dheGroups = TlsUtilities.ReadUint8Array(length, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return dheGroups;
+ }
+
+ public static byte ReadNegotiatedDheGroupsServerExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ if (extensionData.Length != 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return extensionData[0];
+ }
+
+ public static DHParameters GetParametersForDHEGroup(short dheGroup)
+ {
+ switch (dheGroup)
+ {
+ case FiniteFieldDheGroup.ffdhe2432:
+ return draft_ffdhe2432;
+ case FiniteFieldDheGroup.ffdhe3072:
+ return draft_ffdhe3072;
+ case FiniteFieldDheGroup.ffdhe4096:
+ return draft_ffdhe4096;
+ case FiniteFieldDheGroup.ffdhe6144:
+ return draft_ffdhe6144;
+ case FiniteFieldDheGroup.ffdhe8192:
+ return draft_ffdhe8192;
+ default:
+ return null;
+ }
+ }
+
+ public static bool ContainsDheCipherSuites(int[] cipherSuites)
+ {
+ for (int i = 0; i < cipherSuites.Length; ++i)
+ {
+ if (IsDheCipherSuite(cipherSuites[i]))
+ return true;
+ }
+ return false;
+ }
+
+ public static bool IsDheCipherSuite(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ /*
+ * RFC 2246
+ */
+ case CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+
+ /*
+ * RFC 3268
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+
+ /*
+ * RFC 5932
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+
+ /*
+ * RFC 4162
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+
+ /*
+ * RFC 4279
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+
+ /*
+ * RFC 4785
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+
+ /*
+ * RFC 5246
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+
+ /*
+ * RFC 5288
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+
+ /*
+ * RFC 5487
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+
+ /*
+ * RFC 6367
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+
+ /*
+ * RFC 6655
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+
+ /*
+ * draft-agl-tls-chacha20poly1305-04
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+
+ /*
+ * draft-josefsson-salsa20-tls-04
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public static bool AreCompatibleParameters(DHParameters a, DHParameters b)
+ {
+ return a.P.Equals(b.P) && a.G.Equals(b.G);
+ }
+
public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
DHPrivateKeyParameters privateKey)
{
@@ -36,15 +417,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 +447,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(Two)) > 0)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
@@ -71,5 +460,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/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
index ee6d6eb44..3c05bb6f0 100644
--- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -1,53 +1,102 @@
using System;
+using System.Collections;
using System.IO;
-using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- internal class TlsDheKeyExchange
- : TlsDHKeyExchange
+ public class TlsDheKeyExchange
+ : TlsDHKeyExchange
{
- internal TlsDheKeyExchange(TlsClientContext context, int keyExchange)
- : base(context, keyExchange)
+ protected TlsSignerCredentials mServerCredentials = null;
+
+ public TlsDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters)
+ : base(keyExchange, supportedSignatureAlgorithms, dhParameters)
{
}
- public override void SkipServerKeyExchange()
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ if (!(serverCredentials is TlsSignerCredentials))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ ProcessServerCertificate(serverCredentials.Certificate);
+
+ this.mServerCredentials = (TlsSignerCredentials)serverCredentials;
}
- public override void ProcessServerKeyExchange(Stream input)
+ public override byte[] GenerateServerKeyExchange()
{
- SecurityParameters securityParameters = context.SecurityParameters;
+ if (this.mDHParameters == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ DigestInputBuffer buf = new DigestInputBuffer();
- ISigner signer = InitSigner(tlsSigner, securityParameters);
- Stream sigIn = new SignerStream(input, signer, null);
+ this.mDHAgreeServerPrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+ this.mDHParameters, buf);
- byte[] pBytes = TlsUtilities.ReadOpaque16(sigIn);
- byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
- byte[] YsBytes = TlsUtilities.ReadOpaque16(sigIn);
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ IDigest d;
- byte[] sigByte = TlsUtilities.ReadOpaque16(input);
- if (!signer.VerifySignature(sigByte))
+ if (TlsUtilities.IsTlsV12(context))
{
- throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm;
+ if (signatureAndHashAlgorithm == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash);
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ d = new CombinedHash();
}
- BigInteger p = new BigInteger(1, pBytes);
- BigInteger g = new BigInteger(1, gBytes);
- BigInteger Ys = new BigInteger(1, YsBytes);
+ SecurityParameters securityParameters = context.SecurityParameters;
+ d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+ d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+ buf.UpdateDigest(d);
+
+ byte[] hash = DigestUtilities.DoFinal(d);
+
+ byte[] signature = mServerCredentials.GenerateCertificateSignature(hash);
+
+ DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ signed_params.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ SignerInputBuffer buf = new SignerInputBuffer();
+ Stream teeIn = new TeeInputStream(input, buf);
+
+ ServerDHParams dhParams = ServerDHParams.Parse(teeIn);
+
+ DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+
+ ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
+ buf.UpdateSigner(signer);
+ if (!signer.VerifySignature(signed_params.Signature))
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
- this.dhAgreeServerPublicKey = ValidateDHPublicKey(
- new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(dhParams.PublicKey);
}
- protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
+ SecurityParameters securityParameters)
{
- ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+ ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey);
signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
return signer;
diff --git a/crypto/src/crypto/tls/TlsDsaSigner.cs b/crypto/src/crypto/tls/TlsDsaSigner.cs
index bba114e90..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..42dc2f2ef 100644
--- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -3,149 +3,154 @@ using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto.Agreement;
-using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- /**
- * ECDH key exchange (see RFC 4492)
- */
- internal class TlsECDHKeyExchange
- : TlsKeyExchange
+ /// <summary>(D)TLS ECDH key exchange (see RFC 4492).</summary>
+ public class TlsECDHKeyExchange
+ : AbstractTlsKeyExchange
{
- protected TlsClientContext context;
- protected int keyExchange;
- protected TlsSigner tlsSigner;
+ protected TlsSigner mTlsSigner;
+ protected int[] mNamedCurves;
+ protected byte[] mClientECPointFormats, mServerECPointFormats;
- protected AsymmetricKeyParameter serverPublicKey;
- protected ECPublicKeyParameters ecAgreeServerPublicKey;
- protected TlsAgreementCredentials agreementCredentials;
- protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null;
+ protected AsymmetricKeyParameter mServerPublicKey;
+ protected TlsAgreementCredentials mAgreementCredentials;
- internal TlsECDHKeyExchange(TlsClientContext context, int keyExchange)
+ protected ECPrivateKeyParameters mECAgreePrivateKey;
+ protected ECPublicKeyParameters mECAgreePublicKey;
+
+ public TlsECDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves,
+ byte[] clientECPointFormats, byte[] serverECPointFormats)
+ : base(keyExchange, supportedSignatureAlgorithms)
{
switch (keyExchange)
{
- case KeyExchangeAlgorithm.ECDHE_RSA:
- this.tlsSigner = new TlsRsaSigner();
- break;
- case KeyExchangeAlgorithm.ECDHE_ECDSA:
- this.tlsSigner = new TlsECDsaSigner();
- break;
- case KeyExchangeAlgorithm.ECDH_RSA:
- case KeyExchangeAlgorithm.ECDH_ECDSA:
- this.tlsSigner = null;
- break;
- default:
- throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ this.mTlsSigner = new TlsRsaSigner();
+ break;
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ this.mTlsSigner = new TlsECDsaSigner();
+ break;
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ this.mTlsSigner = null;
+ break;
+ default:
+ throw new InvalidOperationException("unsupported key exchange algorithm");
}
- this.context = context;
- this.keyExchange = keyExchange;
+ this.mNamedCurves = namedCurves;
+ this.mClientECPointFormats = clientECPointFormats;
+ this.mServerECPointFormats = serverECPointFormats;
}
- public virtual void SkipServerCertificate()
+ public override void Init(TlsContext context)
+ {
+ base.Init(context);
+
+ if (this.mTlsSigner != null)
+ {
+ this.mTlsSigner.Init(context);
+ }
+ }
+
+ public override void SkipServerCredentials()
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ public override void ProcessServerCertificate(Certificate serverCertificate)
{
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
- SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
try
{
- this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
}
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
- if (tlsSigner == null)
+ if (mTlsSigner == null)
{
try
{
- this.ecAgreeServerPublicKey = ValidateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+ this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey((ECPublicKeyParameters) this.mServerPublicKey);
}
- catch (InvalidCastException)
+ catch (InvalidCastException e)
{
- throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
}
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement);
}
else
{
- if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
- {
+ if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey))
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
- }
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
}
-
- // TODO
- /*
- * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
- * signing algorithm for the certificate must be the same as the algorithm for the
- * certificate key."
- */
- }
-
- public virtual void SkipServerKeyExchange()
- {
- // do nothing
+
+ base.ProcessServerCertificate(serverCertificate);
}
- public virtual void ProcessServerKeyExchange(Stream input)
+ public override bool RequiresServerKeyExchange
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ get
+ {
+ switch (mKeyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.ECDH_anon:
+ return true;
+ default:
+ return false;
+ }
+ }
}
- public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
{
/*
- * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
- * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
- * prohibited because the use of a long-term ECDH client key would jeopardize the
- * forward secrecy property of these algorithms.
+ * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
+ * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
+ * the use of a long-term ECDH client key would jeopardize the forward secrecy property of
+ * these algorithms.
*/
byte[] types = certificateRequest.CertificateTypes;
- foreach (byte type in types)
+ for (int i = 0; i < types.Length; ++i)
{
- switch (type)
+ switch (types[i])
{
- case ClientCertificateType.rsa_sign:
- case ClientCertificateType.dss_sign:
- case ClientCertificateType.ecdsa_sign:
- case ClientCertificateType.rsa_fixed_ecdh:
- case ClientCertificateType.ecdsa_fixed_ecdh:
- break;
- default:
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_fixed_ecdh:
+ case ClientCertificateType.ecdsa_fixed_ecdh:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
}
- public virtual void SkipClientCredentials()
- {
- this.agreementCredentials = null;
- }
-
- public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
{
if (clientCredentials is TlsAgreementCredentials)
{
- // TODO Validate client cert has matching parameters (see 'AreOnSameCurve')?
+ // TODO Validate client cert has matching parameters (see 'TlsEccUtilities.AreOnSameCurve')?
- this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials;
}
else if (clientCredentials is TlsSignerCredentials)
{
@@ -157,80 +162,50 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
- public virtual void GenerateClientKeyExchange(Stream output)
+ public override void GenerateClientKeyExchange(Stream output)
{
- if (agreementCredentials == null)
+ if (mAgreementCredentials == null)
{
- GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output);
+ this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+ mServerECPointFormats, mECAgreePublicKey.Parameters, output);
}
}
- public virtual byte[] GeneratePremasterSecret()
+ public override void ProcessClientCertificate(Certificate clientCertificate)
{
- if (agreementCredentials != null)
- {
- return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey);
- }
-
- return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
- }
-
- protected virtual bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
- {
- // TODO Move to ECDomainParameters.Equals() or other utility method?
- return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H);
+ // TODO Extract the public key
+ // TODO If the certificate is 'fixed', take the public key as mECAgreeClientPublicKey
}
- protected virtual byte[] ExternalizeKey(ECPublicKeyParameters keyParameters)
+ public override void ProcessClientKeyExchange(Stream input)
{
- // TODO Add support for compressed encoding and SPF extension
-
- /*
- * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format.
- * Here, the format MUST conform to what the server has requested through a
- * Supported Point Formats Extension if this extension was used, and MUST be
- * uncompressed if this extension was not used.
- */
- return keyParameters.Q.GetEncoded();
- }
+ if (mECAgreePublicKey != null)
+ {
+ // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate
+ return;
+ }
- protected virtual AsymmetricCipherKeyPair GenerateECKeyPair(ECDomainParameters ecParams)
- {
- ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
- ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams,
- context.SecureRandom);
- keyPairGenerator.Init(keyGenerationParameters);
- return keyPairGenerator.GenerateKeyPair();
- }
+ byte[] point = TlsUtilities.ReadOpaque8(input);
- protected virtual void GenerateEphemeralClientKeyExchange(ECDomainParameters ecParams, Stream output)
- {
- AsymmetricCipherKeyPair ecAgreeClientKeyPair = GenerateECKeyPair(ecParams);
- this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.Private;
+ ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters;
- byte[] keData = ExternalizeKey((ECPublicKeyParameters)ecAgreeClientKeyPair.Public);
- TlsUtilities.WriteOpaque8(keData, output);
+ this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
+ mServerECPointFormats, curve_params, point));
}
- protected virtual byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey,
- ECPrivateKeyParameters privateKey)
+ public override byte[] GeneratePremasterSecret()
{
- ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
- basicAgreement.Init(privateKey);
- BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey);
+ if (mAgreementCredentials != null)
+ {
+ return mAgreementCredentials.GenerateAgreement(mECAgreePublicKey);
+ }
- /*
- * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by
- * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for
- * any given field; leading zeros found in this octet string MUST NOT be truncated.
- */
- return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
- }
+ if (mECAgreePrivateKey != null)
+ {
+ return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey);
+ }
- protected virtual ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
- {
- // TODO Check RFC 4492 for validation
- return key;
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
}
diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index a36bff75b..0644bd44d 100644
--- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -2,91 +2,186 @@ using System;
using System.Collections;
using System.IO;
-using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- /**
- * ECDHE key exchange (see RFC 4492)
- */
- internal class TlsECDheKeyExchange : TlsECDHKeyExchange
+ /// <summary>(D)TLS ECDHE key exchange (see RFC 4492).</summary>
+ public class TlsECDheKeyExchange
+ : TlsECDHKeyExchange
{
- internal TlsECDheKeyExchange(TlsClientContext context, int keyExchange)
- : base(context, keyExchange)
- {
- }
+ protected TlsSignerCredentials mServerCredentials = null;
- public override void SkipServerKeyExchange()
+ public TlsECDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves,
+ byte[] clientECPointFormats, byte[] serverECPointFormats)
+ : base(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats)
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public override void ProcessServerKeyExchange(Stream input)
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
{
- SecurityParameters securityParameters = context.SecurityParameters;
+ if (!(serverCredentials is TlsSignerCredentials))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
- ISigner signer = InitSigner(tlsSigner, securityParameters);
- Stream sigIn = new SignerStream(input, signer, null);
+ ProcessServerCertificate(serverCredentials.Certificate);
- byte curveType = TlsUtilities.ReadUint8(sigIn);
- ECDomainParameters curve_params;
+ this.mServerCredentials = (TlsSignerCredentials)serverCredentials;
+ }
- // Currently, we only support named curves
- if (curveType == ECCurveType.named_curve)
+ public override byte[] GenerateServerKeyExchange()
+ {
+ /*
+ * First we try to find a supported named curve from the client's list.
+ */
+ int namedCurve = -1;
+ if (mNamedCurves == null)
{
- int namedCurve = TlsUtilities.ReadUint16(sigIn);
-
- // TODO Check namedCurve is one we offered?
+ // 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
{
- // TODO Add support for explicit curve parameters (read from sigIn)
+ /*
+ * 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);
+ }
+ }
- throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ 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);
}
- byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn);
+ AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(context.SecureRandom, curve_params);
+ this.mECAgreePrivateKey = (ECPrivateKeyParameters)kp.Private;
- byte[] sigByte = TlsUtilities.ReadOpaque16(input);
- if (!signer.VerifySignature(sigByte))
+ DigestInputBuffer buf = new DigestInputBuffer();
+
+ if (namedCurve < 0)
{
- throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ TlsEccUtilities.WriteExplicitECParameters(mClientECPointFormats, curve_params, buf);
+ }
+ else
+ {
+ TlsEccUtilities.WriteNamedECParameters(namedCurve, buf);
}
- // TODO Check curve_params not null
+ ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
+ TlsEccUtilities.WriteECPoint(mClientECPointFormats, ecPublicKey.Q, buf);
- ECPoint Q = curve_params.Curve.DecodePoint(publicBytes);
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ IDigest d;
+
+ if (TlsUtilities.IsTlsV12(context))
+ {
+ signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm;
+ if (signatureAndHashAlgorithm == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
- this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params));
+ d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash);
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ d = new CombinedHash();
+ }
+
+ SecurityParameters securityParameters = context.SecurityParameters;
+ d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+ d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+ buf.UpdateDigest(d);
+
+ byte[] hash = DigestUtilities.DoFinal(d);
+
+ byte[] signature = mServerCredentials.GenerateCertificateSignature(hash);
+
+ DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ signed_params.Encode(buf);
+
+ return buf.ToArray();
}
-
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ SignerInputBuffer buf = new SignerInputBuffer();
+ Stream teeIn = new TeeInputStream(input, buf);
+
+ ECDomainParameters curve_params = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, teeIn);
+
+ byte[] point = TlsUtilities.ReadOpaque8(teeIn);
+
+ DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+
+ ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
+ buf.UpdateSigner(signer);
+ if (!signer.VerifySignature(signed_params.Signature))
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+
+ this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
+ mClientECPointFormats, curve_params, point));
+ }
+
public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
{
/*
- * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
- * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
- * prohibited because the use of a long-term ECDH client key would jeopardize the
- * forward secrecy property of these algorithms.
+ * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
+ * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
+ * the use of a long-term ECDH client key would jeopardize the forward secrecy property of
+ * these algorithms.
*/
byte[] types = certificateRequest.CertificateTypes;
- foreach (byte type in types)
+ for (int i = 0; i < types.Length; ++i)
{
- switch (type)
+ switch (types[i])
{
- case ClientCertificateType.rsa_sign:
- case ClientCertificateType.dss_sign:
- case ClientCertificateType.ecdsa_sign:
- break;
- default:
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
}
-
+
public override void ProcessClientCredentials(TlsCredentials clientCredentials)
{
if (clientCredentials is TlsSignerCredentials)
@@ -99,9 +194,10 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
- protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
+ SecurityParameters securityParameters)
{
- ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+ ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey);
signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
return signer;
diff --git a/crypto/src/crypto/tls/TlsECDsaSigner.cs b/crypto/src/crypto/tls/TlsECDsaSigner.cs
index 3c30fdc0c..fa9d0b714 100644
--- a/crypto/src/crypto/tls/TlsECDsaSigner.cs
+++ b/crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers;
namespace Org.BouncyCastle.Crypto.Tls
{
- internal class TlsECDsaSigner
- : TlsDsaSigner
- {
- public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
- {
- return publicKey is ECPublicKeyParameters;
- }
+ public class TlsECDsaSigner
+ : TlsDsaSigner
+ {
+ public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey is ECPublicKeyParameters;
+ }
- protected override IDsa CreateDsaImpl()
- {
- return new ECDsaSigner();
- }
- }
+ protected override IDsa CreateDsaImpl(byte hashAlgorithm)
+ {
+ return new ECDsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm)));
+ }
+
+ protected override byte SignatureAlgorithm
+ {
+ get { return Tls.SignatureAlgorithm.ecdsa; }
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
index 909a1002c..64c3c1593 100644
--- a/crypto/src/crypto/tls/TlsEccUtilities.cs
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -16,31 +16,31 @@ 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",
+ private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
"sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
"sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
"secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
"brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
- public static void AddSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
+ public static void AddSupportedEllipticCurvesExtension(IDictionary extensions, int[] namedCurves)
{
extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves);
}
- public static void AddSupportedPointFormatsExtension(Hashtable extensions, byte[] ecPointFormats)
+ public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats)
{
extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
}
- public static int[] GetSupportedEllipticCurvesExtension(Hashtable extensions)
+ public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions)
{
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves);
return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData);
}
- public static byte[] GetSupportedPointFormatsExtension(Hashtable extensions)
+ public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions)
{
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
@@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Tls
int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf);
- TlsProtocolHandler.AssertEmpty(buf);
+ TlsProtocol.AssertEmpty(buf);
return namedCurves;
}
@@ -101,7 +101,7 @@ namespace Org.BouncyCastle.Crypto.Tls
byte[] ecPointFormats = TlsUtilities.ReadUint8Array(length, buf);
- TlsProtocolHandler.AssertEmpty(buf);
+ TlsProtocol.AssertEmpty(buf);
if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed))
{
@@ -117,7 +117,7 @@ namespace Org.BouncyCastle.Crypto.Tls
public static string GetNameOfNamedCurve(int namedCurve)
{
- return IsSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null;
+ return IsSupportedNamedCurve(namedCurve) ? CurveNames[namedCurve - 1] : null;
}
public static ECDomainParameters GetParametersForNamedCurve(int namedCurve)
@@ -142,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Tls
public static bool HasAnySupportedNamedCurves()
{
- return curveNames.Length > 0;
+ return CurveNames.Length > 0;
}
public static bool ContainsEccCipherSuites(int[] cipherSuites)
@@ -276,7 +276,7 @@ namespace Org.BouncyCastle.Crypto.Tls
public static bool IsSupportedNamedCurve(int namedCurve)
{
- return (namedCurve > 0 && namedCurve <= curveNames.Length);
+ return (namedCurve > 0 && namedCurve <= CurveNames.Length);
}
public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat)
@@ -372,8 +372,11 @@ namespace Org.BouncyCastle.Crypto.Tls
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- if (!Arrays.Contains(ecPointFormats, actualFormat))
+ if (actualFormat != ECPointFormat.uncompressed
+ && (ecPointFormats == null || !Arrays.Contains(ecPointFormats, actualFormat)))
+ {
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
return curve.DecodePoint(encoding);
}
@@ -386,9 +389,9 @@ namespace Org.BouncyCastle.Crypto.Tls
ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding);
return new ECPublicKeyParameters(Y, curve_params);
}
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
@@ -528,9 +531,9 @@ namespace Org.BouncyCastle.Crypto.Tls
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
diff --git a/crypto/src/crypto/tls/TlsEncryptionCredentials.cs b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs
new file mode 100644
index 000000000..52f007006
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsEncryptionCredentials
+ : TlsCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret);
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
new file mode 100644
index 000000000..46851b66c
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
@@ -0,0 +1,258 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class TlsExtensionsUtilities
+ {
+ public static IDictionary EnsureExtensionsInitialised(IDictionary extensions)
+ {
+ return extensions == null ? Platform.CreateHashtable() : extensions;
+ }
+
+ public static void AddEncryptThenMacExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.encrypt_then_mac] = CreateEncryptThenMacExtension();
+ }
+
+ public static void AddExtendedMasterSecretExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.extended_master_secret] = CreateExtendedMasterSecretExtension();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension)
+ {
+ extensions[ExtensionType.heartbeat] = CreateHeartbeatExtension(heartbeatExtension);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddMaxFragmentLengthExtension(IDictionary extensions, byte maxFragmentLength)
+ {
+ extensions[ExtensionType.max_fragment_length] = CreateMaxFragmentLengthExtension(maxFragmentLength);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddServerNameExtension(IDictionary extensions, ServerNameList serverNameList)
+ {
+ extensions[ExtensionType.server_name] = CreateServerNameExtension(serverNameList);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddStatusRequestExtension(IDictionary extensions, CertificateStatusRequest statusRequest)
+ {
+ extensions[ExtensionType.status_request] = CreateStatusRequestExtension(statusRequest);
+ }
+
+ public static void AddTruncatedHMacExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.truncated_hmac] = CreateTruncatedHMacExtension();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static HeartbeatExtension GetHeartbeatExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.heartbeat);
+ return extensionData == null ? null : ReadHeartbeatExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static short GetMaxFragmentLengthExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.max_fragment_length);
+ return extensionData == null ? (short)-1 : (short)ReadMaxFragmentLengthExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static ServerNameList GetServerNameExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name);
+ return extensionData == null ? null : ReadServerNameExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static CertificateStatusRequest GetStatusRequestExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request);
+ return extensionData == null ? null : ReadStatusRequestExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool HasEncryptThenMacExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.encrypt_then_mac);
+ return extensionData == null ? false : ReadEncryptThenMacExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool HasExtendedMasterSecretExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.extended_master_secret);
+ return extensionData == null ? false : ReadExtendedMasterSecretExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool HasTruncatedHMacExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac);
+ return extensionData == null ? false : ReadTruncatedHMacExtension(extensionData);
+ }
+
+ public static byte[] CreateEmptyExtensionData()
+ {
+ return TlsUtilities.EmptyBytes;
+ }
+
+ public static byte[] CreateEncryptThenMacExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ public static byte[] CreateExtendedMasterSecretExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension)
+ {
+ if (heartbeatExtension == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ heartbeatExtension.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateMaxFragmentLengthExtension(byte maxFragmentLength)
+ {
+ return new byte[]{ maxFragmentLength };
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateServerNameExtension(ServerNameList serverNameList)
+ {
+ if (serverNameList == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ serverNameList.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateStatusRequestExtension(CertificateStatusRequest statusRequest)
+ {
+ if (statusRequest == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ MemoryStream buf = new MemoryStream();
+
+ statusRequest.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ public static byte[] CreateTruncatedHMacExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"></exception>
+ private static bool ReadEmptyExtensionData(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ if (extensionData.Length != 0)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return true;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool ReadEncryptThenMacExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool ReadExtendedMasterSecretExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static HeartbeatExtension ReadHeartbeatExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ HeartbeatExtension heartbeatExtension = HeartbeatExtension.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return heartbeatExtension;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static short ReadMaxFragmentLengthExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ if (extensionData.Length != 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return extensionData[0];
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static ServerNameList ReadServerNameExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ ServerNameList serverNameList = ServerNameList.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return serverNameList;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static CertificateStatusRequest ReadStatusRequestExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ CertificateStatusRequest statusRequest = CertificateStatusRequest.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return statusRequest;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool ReadTruncatedHMacExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsFatalAlert.cs b/crypto/src/crypto/tls/TlsFatalAlert.cs
index 4fb2a41bd..55d784dd9 100644
--- a/crypto/src/crypto/tls/TlsFatalAlert.cs
+++ b/crypto/src/crypto/tls/TlsFatalAlert.cs
@@ -9,6 +9,12 @@ namespace Org.BouncyCastle.Crypto.Tls
private readonly byte alertDescription;
public TlsFatalAlert(byte alertDescription)
+ : this(alertDescription, null)
+ {
+ }
+
+ public TlsFatalAlert(byte alertDescription, Exception alertCause)
+ : base(Tls.AlertDescription.GetText(alertDescription), alertCause)
{
this.alertDescription = alertDescription;
}
diff --git a/crypto/src/crypto/tls/TlsHandshakeHash.cs b/crypto/src/crypto/tls/TlsHandshakeHash.cs
new file mode 100644
index 000000000..7118d9769
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsHandshakeHash.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsHandshakeHash
+ : IDigest
+ {
+ void Init(TlsContext context);
+
+ TlsHandshakeHash NotifyPrfDetermined();
+
+ void TrackHashAlgorithm(byte hashAlgorithm);
+
+ void SealHashAlgorithms();
+
+ TlsHandshakeHash StopTracking();
+
+ IDigest ForkPrfHash();
+
+ byte[] GetFinalHash(byte hashAlgorithm);
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsKeyExchange.cs b/crypto/src/crypto/tls/TlsKeyExchange.cs
index 5102edbec..6731f6f63 100644
--- a/crypto/src/crypto/tls/TlsKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -3,36 +3,52 @@ using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <summary>
- /// A generic interface for key exchange implementations in TLS 1.0.
- /// </summary>
- public interface TlsKeyExchange
- {
- /// <exception cref="IOException"/>
- void SkipServerCertificate();
-
- /// <exception cref="IOException"/>
- void ProcessServerCertificate(Certificate serverCertificate);
-
- /// <exception cref="IOException"/>
- void SkipServerKeyExchange();
-
- /// <exception cref="IOException"/>
- void ProcessServerKeyExchange(Stream input);
-
- /// <exception cref="IOException"/>
- void ValidateCertificateRequest(CertificateRequest certificateRequest);
-
- /// <exception cref="IOException"/>
- void SkipClientCredentials();
-
- /// <exception cref="IOException"/>
- void ProcessClientCredentials(TlsCredentials clientCredentials);
-
- /// <exception cref="IOException"/>
- void GenerateClientKeyExchange(Stream output);
-
- /// <exception cref="IOException"/>
- byte[] GeneratePremasterSecret();
- }
+ /// <summary>
+ /// A generic interface for key exchange implementations in (D)TLS.
+ /// </summary>
+ public interface TlsKeyExchange
+ {
+ void Init(TlsContext context);
+
+ /// <exception cref="IOException"/>
+ void SkipServerCredentials();
+
+ /// <exception cref="IOException"/>
+ void ProcessServerCredentials(TlsCredentials serverCredentials);
+
+ /// <exception cref="IOException"/>
+ void ProcessServerCertificate(Certificate serverCertificate);
+
+ bool RequiresServerKeyExchange { get; }
+
+ /// <exception cref="IOException"/>
+ byte[] GenerateServerKeyExchange();
+
+ /// <exception cref="IOException"/>
+ void SkipServerKeyExchange();
+
+ /// <exception cref="IOException"/>
+ void ProcessServerKeyExchange(Stream input);
+
+ /// <exception cref="IOException"/>
+ void ValidateCertificateRequest(CertificateRequest certificateRequest);
+
+ /// <exception cref="IOException"/>
+ void SkipClientCredentials();
+
+ /// <exception cref="IOException"/>
+ void ProcessClientCredentials(TlsCredentials clientCredentials);
+
+ /// <exception cref="IOException"/>
+ void ProcessClientCertificate(Certificate clientCertificate);
+
+ /// <exception cref="IOException"/>
+ void GenerateClientKeyExchange(Stream output);
+
+ /// <exception cref="IOException"/>
+ void ProcessClientKeyExchange(Stream input);
+
+ /// <exception cref="IOException"/>
+ byte[] GeneratePremasterSecret();
+ }
}
diff --git a/crypto/src/crypto/tls/TlsMac.cs b/crypto/src/crypto/tls/TlsMac.cs
index e4313617e..a80319a17 100644
--- a/crypto/src/crypto/tls/TlsMac.cs
+++ b/crypto/src/crypto/tls/TlsMac.cs
@@ -9,134 +9,165 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <remarks>
- /// A generic TLS MAC implementation, which can be used with any kind of
- /// IDigest to act as an HMAC.
- /// </remarks>
+ /// <summary>
+ /// A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest.
+ /// </summary>
public class TlsMac
{
- protected long seqNo;
- protected byte[] secret;
- protected HMac mac;
+ protected readonly TlsContext context;
+ protected readonly byte[] secret;
+ protected readonly IMac mac;
+ protected readonly int digestBlockSize;
+ protected readonly int digestOverhead;
+ protected readonly int macLength;
/**
- * Generate a new instance of an TlsMac.
- *
- * @param digest The digest to use.
- * @param key_block A byte-array where the key for this mac is located.
- * @param offset The number of bytes to skip, before the key starts in the buffer.
- * @param len The length of the key.
- */
- public TlsMac(
- IDigest digest,
- byte[] key_block,
- int offset,
- int len)
+ * Generate a new instance of an TlsMac.
+ *
+ * @param context the TLS client context
+ * @param digest The digest to use.
+ * @param key A byte-array where the key for this MAC is located.
+ * @param keyOff The number of bytes to skip, before the key starts in the buffer.
+ * @param keyLen The length of the key.
+ */
+ public TlsMac(TlsContext context, IDigest digest, byte[] key, int keyOff, int keyLen)
{
- this.seqNo = 0;
+ this.context = context;
- KeyParameter param = new KeyParameter(key_block, offset, len);
+ KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen);
- this.secret = Arrays.Clone(param.GetKey());
+ this.secret = Arrays.Clone(keyParameter.GetKey());
- this.mac = new HMac(digest);
- this.mac.Init(param);
- }
+ // TODO This should check the actual algorithm, not rely on the engine type
+ if (digest is LongDigest)
+ {
+ this.digestBlockSize = 128;
+ this.digestOverhead = 16;
+ }
+ else
+ {
+ this.digestBlockSize = 64;
+ this.digestOverhead = 8;
+ }
- /**
- * @return the MAC write secret
- */
- public virtual byte[] GetMacSecret()
- {
- return this.secret;
- }
+ if (TlsUtilities.IsSsl(context))
+ {
+ this.mac = new Ssl3Mac(digest);
- /**
- * @return the current write sequence number
- */
- public virtual long SequenceNumber
- {
- get { return this.seqNo; }
+ // TODO This should check the actual algorithm, not assume based on the digest size
+ if (digest.GetDigestSize() == 20)
+ {
+ /*
+ * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not
+ * digest block-aligned.
+ */
+ this.digestOverhead = 4;
+ }
+ }
+ else
+ {
+ this.mac = new HMac(digest);
+
+ // NOTE: The input pad for HMAC is always a full digest block
+ }
+
+ this.mac.Init(keyParameter);
+
+ this.macLength = mac.GetMacSize();
+ if (context.SecurityParameters.truncatedHMac)
+ {
+ this.macLength = System.Math.Min(this.macLength, 10);
+ }
}
/**
- * Increment the current write sequence number
+ * @return the MAC write secret
*/
- public virtual void IncSequenceNumber()
+ public virtual byte[] MacSecret
{
- this.seqNo++;
+ get { return this.secret; }
}
/**
- * @return The Keysize of the mac.
- */
+ * @return The output length of this MAC.
+ */
public virtual int Size
{
- get { return mac.GetMacSize(); }
+ get { return macLength; }
}
/**
- * Calculate the mac for some given data.
- * <p/>
- * TlsMac will keep track of the sequence number internally.
- *
- * @param type The message type of the message.
- * @param message A byte-buffer containing the message.
- * @param offset The number of bytes to skip, before the message starts.
- * @param len The length of the message.
- * @return A new byte-buffer containing the mac value.
- */
- public virtual byte[] CalculateMac(byte type, byte[] message, int offset, int len)
+ * Calculate the MAC for some given data.
+ *
+ * @param type The message type of the message.
+ * @param message A byte-buffer containing the message.
+ * @param offset The number of bytes to skip, before the message starts.
+ * @param length The length of the message.
+ * @return A new byte-buffer containing the MAC value.
+ */
+ public virtual byte[] CalculateMac(long seqNo, byte type, byte[] message, int offset, int length)
{
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
+ ProtocolVersion serverVersion = context.ServerVersion;
+ bool isSsl = serverVersion.IsSsl;
- byte[] macHeader = new byte[isTls ? 13 : 11];
- TlsUtilities.WriteUint64(seqNo++, macHeader, 0);
+ byte[] macHeader = new byte[isSsl ? 11 : 13];
+ TlsUtilities.WriteUint64(seqNo, macHeader, 0);
TlsUtilities.WriteUint8(type, macHeader, 8);
- if (isTls)
+ if (!isSsl)
{
- TlsUtilities.WriteVersion(macHeader, 9);
+ TlsUtilities.WriteVersion(serverVersion, macHeader, 9);
}
- TlsUtilities.WriteUint16(len, macHeader, 11);
+ TlsUtilities.WriteUint16(length, macHeader, macHeader.Length - 2);
mac.BlockUpdate(macHeader, 0, macHeader.Length);
- mac.BlockUpdate(message, offset, len);
- return MacUtilities.DoFinal(mac);
+ mac.BlockUpdate(message, offset, length);
+
+ return Truncate(MacUtilities.DoFinal(mac));
}
- public virtual byte[] CalculateMacConstantTime(byte type, byte[] message, int offset, int len,
+ public virtual byte[] CalculateMacConstantTime(long seqNo, byte type, byte[] message, int offset, int length,
int fullLength, byte[] dummyData)
{
- // Actual MAC only calculated on 'len' bytes
- byte[] result = CalculateMac(type, message, offset, len);
+ /*
+ * Actual MAC only calculated on 'length' bytes...
+ */
+ byte[] result = CalculateMac(seqNo, type, message, offset, length);
+
+ /*
+ * ...but ensure a constant number of complete digest blocks are processed (as many as would
+ * be needed for 'fullLength' bytes of input).
+ */
+ int headerLength = TlsUtilities.IsSsl(context) ? 11 : 13;
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
+ // How many extra full blocks do we need to calculate?
+ int extra = GetDigestBlockCount(headerLength + fullLength) - GetDigestBlockCount(headerLength + length);
- // ...but ensure a constant number of complete digest blocks are processed (per 'fullLength')
- if (isTls)
+ while (--extra >= 0)
{
- // TODO Currently all TLS digests use a block size of 64, a suffix (length field) of 8, and padding (1+)
- int db = 64, ds = 8;
+ mac.BlockUpdate(dummyData, 0, digestBlockSize);
+ }
- int L1 = 13 + fullLength;
- int L2 = 13 + len;
+ // One more byte in case the implementation is "lazy" about processing blocks
+ mac.Update(dummyData[0]);
+ mac.Reset();
- // How many extra full blocks do we need to calculate?
- int extra = ((L1 + ds) / db) - ((L2 + ds) / db);
+ return result;
+ }
- while (--extra >= 0)
- {
- mac.BlockUpdate(dummyData, 0, db);
- }
+ protected virtual int GetDigestBlockCount(int inputLength)
+ {
+ // NOTE: This calculation assumes a minimum of 1 pad byte
+ return (inputLength + digestOverhead) / digestBlockSize;
+ }
- // One more byte in case the implementation is "lazy" about processing blocks
- mac.Update(dummyData[0]);
- mac.Reset();
+ protected virtual byte[] Truncate(byte[] bs)
+ {
+ if (bs.Length <= macLength)
+ {
+ return bs;
}
- return result;
+ return Arrays.CopyOf(bs, macLength);
}
}
}
diff --git a/crypto/src/crypto/tls/TlsNullCipher.cs b/crypto/src/crypto/tls/TlsNullCipher.cs
index 3e2bfa847..f30ace24f 100644
--- a/crypto/src/crypto/tls/TlsNullCipher.cs
+++ b/crypto/src/crypto/tls/TlsNullCipher.cs
@@ -1,28 +1,118 @@
using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
/// <summary>
- /// A NULL cipher suite, for use during handshake.
+ /// A NULL CipherSuite, with optional MAC.
/// </summary>
public class TlsNullCipher
- : TlsCipher
+ : TlsCipher
{
- public virtual byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len)
+ protected readonly TlsContext context;
+
+ protected readonly TlsMac writeMac;
+ protected readonly TlsMac readMac;
+
+ public TlsNullCipher(TlsContext context)
{
- return CopyData(plaintext, offset, len);
+ this.context = context;
+ this.writeMac = null;
+ this.readMac = null;
}
- public virtual byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len)
+ /// <exception cref="IOException"></exception>
+ public TlsNullCipher(TlsContext context, IDigest clientWriteDigest, IDigest serverWriteDigest)
{
- return CopyData(ciphertext, offset, len);
+ if ((clientWriteDigest == null) != (serverWriteDigest == null))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.context = context;
+
+ TlsMac clientWriteMac = null, serverWriteMac = null;
+
+ if (clientWriteDigest != null)
+ {
+ int key_block_size = clientWriteDigest.GetDigestSize()
+ + serverWriteDigest.GetDigestSize();
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.GetDigestSize());
+ offset += clientWriteDigest.GetDigestSize();
+
+ serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.GetDigestSize());
+ offset += serverWriteDigest.GetDigestSize();
+
+ if (offset != key_block_size)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ if (context.IsServer)
+ {
+ writeMac = serverWriteMac;
+ readMac = clientWriteMac;
+ }
+ else
+ {
+ writeMac = clientWriteMac;
+ readMac = serverWriteMac;
+ }
}
- protected virtual byte[] CopyData(byte[] text, int offset, int len)
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
{
- byte[] result = new byte[len];
- Array.Copy(text, offset, result, 0, len);
+ int result = ciphertextLimit;
+ if (writeMac != null)
+ {
+ result -= writeMac.Size;
+ }
return result;
}
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
+ {
+ if (writeMac == null)
+ {
+ return Arrays.CopyOfRange(plaintext, offset, offset + len);
+ }
+
+ byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len);
+ byte[] ciphertext = new byte[len + mac.Length];
+ Array.Copy(plaintext, offset, ciphertext, 0, len);
+ Array.Copy(mac, 0, ciphertext, len, mac.Length);
+ return ciphertext;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
+ {
+ if (readMac == null)
+ {
+ return Arrays.CopyOfRange(ciphertext, offset, offset + len);
+ }
+
+ int macSize = readMac.Size;
+ if (len < macSize)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int macInputLen = len - macSize;
+
+ byte[] receivedMac = Arrays.CopyOfRange(ciphertext, offset + macInputLen, offset + len);
+ byte[] computedMac = readMac.CalculateMac(seqNo, type, ciphertext, offset, macInputLen);
+
+ if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac))
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ return Arrays.CopyOfRange(ciphertext, offset, offset + macInputLen);
+ }
}
}
diff --git a/crypto/src/crypto/tls/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs
index 5b5c94a44..1ae41a41a 100644
--- a/crypto/src/crypto/tls/TlsPeer.cs
+++ b/crypto/src/crypto/tls/TlsPeer.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
@@ -15,5 +16,47 @@ namespace Org.BouncyCastle.Crypto.Tls
/// random value.
/// </returns>
bool ShouldUseGmtUnixTime();
+
+ /// <summary>
+ /// Report whether the server supports secure renegotiation
+ /// </summary>
+ /// <remarks>
+ /// The protocol handler automatically processes the relevant extensions
+ /// </remarks>
+ /// <param name="secureRenegotiation">
+ /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
+ /// </param>
+ /// <exception cref="IOException"></exception>
+ void NotifySecureRenegotiation(bool secureRenegotiation);
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
+ /// </summary>
+ /// <returns>A <see cref="TlsCompression"/></returns>
+ /// <exception cref="IOException"/>
+ TlsCompression GetCompression();
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
+ /// </summary>
+ /// <returns>A <see cref="TlsCipher"/></returns>
+ /// <exception cref="IOException"/>
+ TlsCipher GetCipher();
+
+ /// <summary>This method will be called when an alert is raised by the protocol.</summary>
+ /// <param name="alertLevel"><see cref="AlertLevel"/></param>
+ /// <param name="alertDescription"><see cref="AlertDescription"/></param>
+ /// <param name="message">A human-readable message explaining what caused this alert. May be null.</param>
+ /// <param name="cause">The <c>Exception</c> that caused this alert to be raised. May be null.</param>
+ void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause);
+
+ /// <summary>This method will be called when an alert is received from the remote peer.</summary>
+ /// <param name="alertLevel"><see cref="AlertLevel"/></param>
+ /// <param name="alertDescription"><see cref="AlertDescription"/></param>
+ void NotifyAlertReceived(byte alertLevel, byte alertDescription);
+
+ /// <summary>Notifies the peer that the handshake has been successfully completed.</summary>
+ /// <exception cref="IOException"></exception>
+ void NotifyHandshakeComplete();
}
}
diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs
index 764892d0b..8ba156952 100644
--- a/crypto/src/crypto/tls/TlsProtocol.cs
+++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -2,24 +2,822 @@
using System.Collections;
using System.IO;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class TlsProtocol
{
+ private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
+
+ /*
+ * Our Connection states
+ */
+ protected const short CS_START = 0;
+ protected const short CS_CLIENT_HELLO = 1;
+ protected const short CS_SERVER_HELLO = 2;
+ protected const short CS_SERVER_SUPPLEMENTAL_DATA = 3;
+ protected const short CS_SERVER_CERTIFICATE = 4;
+ protected const short CS_CERTIFICATE_STATUS = 5;
+ protected const short CS_SERVER_KEY_EXCHANGE = 6;
+ protected const short CS_CERTIFICATE_REQUEST = 7;
+ protected const short CS_SERVER_HELLO_DONE = 8;
+ protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 9;
+ protected const short CS_CLIENT_CERTIFICATE = 10;
+ protected const short CS_CLIENT_KEY_EXCHANGE = 11;
+ protected const short CS_CERTIFICATE_VERIFY = 12;
+ protected const short CS_CLIENT_FINISHED = 13;
+ protected const short CS_SERVER_SESSION_TICKET = 14;
+ protected const short CS_SERVER_FINISHED = 15;
+ protected const short CS_END = 16;
+
+ /*
+ * Queues for data from some protocols.
+ */
+ private ByteQueue mApplicationDataQueue = new ByteQueue();
+ private ByteQueue mAlertQueue = new ByteQueue(2);
+ private ByteQueue mHandshakeQueue = new ByteQueue();
+ // private ByteQueue mHeartbeatQueue = new ByteQueue();
+
+ /*
+ * The Record Stream we use
+ */
+ internal RecordStream mRecordStream;
+ protected SecureRandom mSecureRandom;
+
+ private TlsStream mTlsStream = null;
+
+ private volatile bool mClosed = false;
+ private volatile bool mFailedWithError = false;
+ private volatile bool mAppDataReady = false;
+ private volatile bool mSplitApplicationDataRecords = true;
+ private byte[] mExpectedVerifyData = null;
+
+ protected TlsSession mTlsSession = null;
+ protected SessionParameters mSessionParameters = null;
+ protected SecurityParameters mSecurityParameters = null;
+ protected Certificate mPeerCertificate = null;
+
+ protected int[] mOfferedCipherSuites = null;
+ protected byte[] mOfferedCompressionMethods = null;
+ protected IDictionary mClientExtensions = null;
+ protected IDictionary mServerExtensions = null;
+
+ protected short mConnectionState = CS_START;
+ protected bool mResumedSession = false;
+ protected bool mReceivedChangeCipherSpec = false;
+ protected bool mSecureRenegotiation = false;
+ protected bool mAllowCertificateStatus = false;
+ protected bool mExpectSessionTicket = false;
+
+ public TlsProtocol(Stream stream, SecureRandom secureRandom)
+ : this(stream, stream, secureRandom)
+ {
+ }
+
+ public TlsProtocol(Stream input, Stream output, SecureRandom secureRandom)
+ {
+ this.mRecordStream = new RecordStream(this, input, output);
+ this.mSecureRandom = secureRandom;
+ }
+
+ protected abstract TlsContext Context { get; }
+
+ internal abstract AbstractTlsContext ContextAdmin { get; }
+
+ protected abstract TlsPeer Peer { get; }
+
+ protected virtual void HandleChangeCipherSpecMessage()
+ {
+ }
+
+ protected abstract void HandleHandshakeMessage(byte type, byte[] buf);
+
+ protected virtual void HandleWarningMessage(byte description)
+ {
+ }
+
+ protected virtual void CleanupHandshake()
+ {
+ if (this.mExpectedVerifyData != null)
+ {
+ Arrays.Fill(this.mExpectedVerifyData, (byte)0);
+ this.mExpectedVerifyData = null;
+ }
+
+ this.mSecurityParameters.Clear();
+ this.mPeerCertificate = null;
+
+ this.mOfferedCipherSuites = null;
+ this.mOfferedCompressionMethods = null;
+ this.mClientExtensions = null;
+ this.mServerExtensions = null;
+
+ this.mResumedSession = false;
+ this.mReceivedChangeCipherSpec = false;
+ this.mSecureRenegotiation = false;
+ this.mAllowCertificateStatus = false;
+ this.mExpectSessionTicket = false;
+ }
+
+ protected virtual void CompleteHandshake()
+ {
+ try
+ {
+ /*
+ * We will now read data, until we have completed the handshake.
+ */
+ while (this.mConnectionState != CS_END)
+ {
+ if (this.mClosed)
+ {
+ // TODO What kind of exception/alert?
+ }
+
+ SafeReadRecord();
+ }
+
+ this.mRecordStream.FinaliseHandshake();
+
+ this.mSplitApplicationDataRecords = !TlsUtilities.IsTlsV11(Context);
+
+ /*
+ * If this was an initial handshake, we are now ready to send and receive application data.
+ */
+ if (!mAppDataReady)
+ {
+ this.mAppDataReady = true;
+
+ this.mTlsStream = new TlsStream(this);
+ }
+
+ if (this.mTlsSession != null)
+ {
+ if (this.mSessionParameters == null)
+ {
+ this.mSessionParameters = new SessionParameters.Builder()
+ .SetCipherSuite(this.mSecurityParameters.cipherSuite)
+ .SetCompressionAlgorithm(this.mSecurityParameters.compressionAlgorithm)
+ .SetMasterSecret(this.mSecurityParameters.masterSecret)
+ .SetPeerCertificate(this.mPeerCertificate)
+ // TODO Consider filtering extensions that aren't relevant to resumed sessions
+ .SetServerExtensions(this.mServerExtensions)
+ .Build();
+
+ this.mTlsSession = new TlsSessionImpl(this.mTlsSession.SessionID, this.mSessionParameters);
+ }
+
+ ContextAdmin.SetResumableSession(this.mTlsSession);
+ }
+
+ Peer.NotifyHandshakeComplete();
+ }
+ finally
+ {
+ CleanupHandshake();
+ }
+ }
+
+ protected internal void ProcessRecord(byte protocol, byte[] buf, int offset, int len)
+ {
+ /*
+ * Have a look at the protocol type, and add it to the correct queue.
+ */
+ switch (protocol)
+ {
+ case ContentType.alert:
+ {
+ mAlertQueue.AddData(buf, offset, len);
+ ProcessAlert();
+ break;
+ }
+ case ContentType.application_data:
+ {
+ if (!mAppDataReady)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ mApplicationDataQueue.AddData(buf, offset, len);
+ ProcessApplicationData();
+ break;
+ }
+ case ContentType.change_cipher_spec:
+ {
+ ProcessChangeCipherSpec(buf, offset, len);
+ break;
+ }
+ case ContentType.handshake:
+ {
+ mHandshakeQueue.AddData(buf, offset, len);
+ ProcessHandshake();
+ break;
+ }
+ case ContentType.heartbeat:
+ {
+ if (!mAppDataReady)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ // TODO[RFC 6520]
+ // mHeartbeatQueue.AddData(buf, offset, len);
+ // ProcessHeartbeat();
+ break;
+ }
+ default:
+ /*
+ * Uh, we don't know this protocol.
+ *
+ * RFC2246 defines on page 13, that we should ignore this.
+ */
+ break;
+ }
+ }
+
+ private void ProcessHandshake()
+ {
+ bool read;
+ do
+ {
+ read = false;
+ /*
+ * We need the first 4 bytes, they contain type and length of the message.
+ */
+ if (mHandshakeQueue.Available >= 4)
+ {
+ byte[] beginning = new byte[4];
+ mHandshakeQueue.Read(beginning, 0, 4, 0);
+ byte type = TlsUtilities.ReadUint8(beginning, 0);
+ int len = TlsUtilities.ReadUint24(beginning, 1);
+
+ /*
+ * Check if we have enough bytes in the buffer to read the full message.
+ */
+ if (mHandshakeQueue.Available >= (len + 4))
+ {
+ /*
+ * Read the message.
+ */
+ byte[] buf = mHandshakeQueue.RemoveData(len, 4);
+
+ /*
+ * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
+ * starting at client hello up to, but not including, this finished message.
+ * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes.
+ */
+ switch (type)
+ {
+ case HandshakeType.hello_request:
+ break;
+ case HandshakeType.finished:
+ default:
+ if (type == HandshakeType.finished && this.mExpectedVerifyData == null)
+ {
+ this.mExpectedVerifyData = CreateVerifyData(!Context.IsServer);
+ }
+
+ mRecordStream.UpdateHandshakeData(beginning, 0, 4);
+ mRecordStream.UpdateHandshakeData(buf, 0, len);
+ break;
+ }
+
+ /*
+ * Now, parse the message.
+ */
+ HandleHandshakeMessage(type, buf);
+ read = true;
+ }
+ }
+ }
+ while (read);
+ }
+
+ private void ProcessApplicationData()
+ {
+ /*
+ * There is nothing we need to do here.
+ *
+ * This function could be used for callbacks when application data arrives in the future.
+ */
+ }
+
+ private void ProcessAlert()
+ {
+ while (mAlertQueue.Available >= 2)
+ {
+ /*
+ * An alert is always 2 bytes. Read the alert.
+ */
+ byte[] tmp = mAlertQueue.RemoveData(2, 0);
+ byte level = tmp[0];
+ byte description = tmp[1];
+
+ Peer.NotifyAlertReceived(level, description);
+
+ if (level == AlertLevel.fatal)
+ {
+ /*
+ * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+ * without proper close_notify messages with level equal to warning.
+ */
+ InvalidateSession();
+
+ this.mFailedWithError = true;
+ this.mClosed = true;
+
+ mRecordStream.SafeClose();
+
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+ else
+ {
+
+ /*
+ * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own
+ * and close down the connection immediately, discarding any pending writes.
+ */
+ // TODO Can close_notify be a fatal alert?
+ if (description == AlertDescription.close_notify)
+ {
+ HandleClose(false);
+ }
+
+ /*
+ * If it is just a warning, we continue.
+ */
+ HandleWarningMessage(description);
+ }
+ }
+ }
+
+ /**
+ * This method is called, when a change cipher spec message is received.
+ *
+ * @throws IOException If the message has an invalid content or the handshake is not in the correct
+ * state.
+ */
+ private void ProcessChangeCipherSpec(byte[] buf, int off, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ byte message = TlsUtilities.ReadUint8(buf, off + i);
+
+ if (message != ChangeCipherSpec.change_cipher_spec)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ if (this.mReceivedChangeCipherSpec
+ || mAlertQueue.Available > 0
+ || mHandshakeQueue.Available > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ mRecordStream.ReceivedReadCipherSpec();
+
+ this.mReceivedChangeCipherSpec = true;
+
+ HandleChangeCipherSpecMessage();
+ }
+ }
+
+ protected internal virtual int ApplicationDataAvailable()
+ {
+ return mApplicationDataQueue.Available;
+ }
+
+ /**
+ * Read data from the network. The method will return immediately, if there is still some data
+ * left in the buffer, or block until some application data has been read from the network.
+ *
+ * @param buf The buffer where the data will be copied to.
+ * @param offset The position where the data will be placed in the buffer.
+ * @param len The maximum number of bytes to read.
+ * @return The number of bytes read.
+ * @throws IOException If something goes wrong during reading data.
+ */
+ protected internal virtual int ReadApplicationData(byte[] buf, int offset, int len)
+ {
+ if (len < 1)
+ return 0;
+
+ while (mApplicationDataQueue.Available == 0)
+ {
+ /*
+ * We need to read some data.
+ */
+ if (this.mClosed)
+ {
+ if (this.mFailedWithError)
+ {
+ /*
+ * Something went terribly wrong, we should throw an IOException
+ */
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ /*
+ * Connection has been closed, there is no more data to read.
+ */
+ return 0;
+ }
+
+ SafeReadRecord();
+ }
+
+ len = System.Math.Min(len, mApplicationDataQueue.Available);
+ mApplicationDataQueue.RemoveData(buf, offset, len, 0);
+ return len;
+ }
+
+ protected virtual void SafeReadRecord()
+ {
+ try
+ {
+ if (!mRecordStream.ReadRecord())
+ {
+ // TODO It would be nicer to allow graceful connection close if between records
+ // this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
+ throw new EndOfStreamException();
+ }
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e);
+ }
+ throw e;
+ }
+ catch (Exception e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
+ }
+ throw e;
+ }
+ }
+
+ protected virtual void SafeWriteRecord(byte type, byte[] buf, int offset, int len)
+ {
+ try
+ {
+ mRecordStream.WriteRecord(type, buf, offset, len);
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to write record", e);
+ }
+ throw e;
+ }
+ catch (Exception e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Send some application data to the remote system.
+ * <p/>
+ * The method will handle fragmentation internally.
+ *
+ * @param buf The buffer with the data.
+ * @param offset The position in the buffer where the data is placed.
+ * @param len The length of the data.
+ * @throws IOException If something goes wrong during sending.
+ */
+ protected internal virtual void WriteData(byte[] buf, int offset, int len)
+ {
+ if (this.mClosed)
+ {
+ if (this.mFailedWithError)
+ throw new IOException(TLS_ERROR_MESSAGE);
+
+ throw new IOException("Sorry, connection has been closed, you cannot write more data");
+ }
+
+ while (len > 0)
+ {
+ /*
+ * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
+ * potentially useful as a traffic analysis countermeasure.
+ *
+ * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
+ */
+
+ if (this.mSplitApplicationDataRecords)
+ {
+ /*
+ * Protect against known IV attack!
+ *
+ * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+ */
+ SafeWriteRecord(ContentType.application_data, buf, offset, 1);
+ ++offset;
+ --len;
+ }
+
+ if (len > 0)
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit());
+ SafeWriteRecord(ContentType.application_data, buf, offset, toWrite);
+ offset += toWrite;
+ len -= toWrite;
+ }
+ }
+ }
+
+ protected virtual void WriteHandshakeMessage(byte[] buf, int off, int len)
+ {
+ while (len > 0)
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit());
+ SafeWriteRecord(ContentType.handshake, buf, off, toWrite);
+ off += toWrite;
+ len -= toWrite;
+ }
+ }
+
+ /// <summary>The secure bidirectional stream for this connection</summary>
+ public virtual Stream Stream
+ {
+ get { return this.mTlsStream; }
+ }
+
/**
- * Make sure the Stream is now empty. Fail otherwise.
- *
- * @param is The Stream to check.
- * @throws IOException If is is not empty.
- */
+ * Terminate this connection with an alert. Can be used for normal closure too.
+ *
+ * @param alertLevel
+ * See {@link AlertLevel} for values.
+ * @param alertDescription
+ * See {@link AlertDescription} for values.
+ * @throws IOException
+ * If alert was fatal.
+ */
+ protected virtual void FailWithError(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ /*
+ * Check if the connection is still open.
+ */
+ if (!mClosed)
+ {
+ /*
+ * Prepare the message
+ */
+ this.mClosed = true;
+
+ if (alertLevel == AlertLevel.fatal)
+ {
+ /*
+ * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+ * without proper close_notify messages with level equal to warning.
+ */
+ // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
+ InvalidateSession();
+
+ this.mFailedWithError = true;
+ }
+ RaiseAlert(alertLevel, alertDescription, message, cause);
+ mRecordStream.SafeClose();
+ if (alertLevel != AlertLevel.fatal)
+ {
+ return;
+ }
+ }
+
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ protected virtual void InvalidateSession()
+ {
+ if (this.mSessionParameters != null)
+ {
+ this.mSessionParameters.Clear();
+ this.mSessionParameters = null;
+ }
+
+ if (this.mTlsSession != null)
+ {
+ this.mTlsSession.Invalidate();
+ this.mTlsSession = null;
+ }
+ }
+
+ protected virtual void ProcessFinishedMessage(MemoryStream buf)
+ {
+ byte[] verify_data = TlsUtilities.ReadFully(mExpectedVerifyData.Length, buf);
+
+ AssertEmpty(buf);
+
+ /*
+ * Compare both checksums.
+ */
+ if (!Arrays.ConstantTimeAreEqual(mExpectedVerifyData, verify_data))
+ {
+ /*
+ * Wrong checksum in the finished message.
+ */
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ protected virtual void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ Peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
+
+ byte[] error = new byte[]{ alertLevel, alertDescription };
+
+ SafeWriteRecord(ContentType.alert, error, 0, 2);
+ }
+
+ protected virtual void RaiseWarning(byte alertDescription, string message)
+ {
+ RaiseAlert(AlertLevel.warning, alertDescription, message, null);
+ }
+
+ protected virtual void SendCertificateMessage(Certificate certificate)
+ {
+ if (certificate == null)
+ {
+ certificate = Certificate.EmptyChain;
+ }
+
+ if (certificate.IsEmpty)
+ {
+ TlsContext context = Context;
+ if (!context.IsServer)
+ {
+ ProtocolVersion serverVersion = Context.ServerVersion;
+ if (serverVersion.IsSsl)
+ {
+ string errorMessage = serverVersion.ToString() + " client didn't provide credentials";
+ RaiseWarning(AlertDescription.no_certificate, errorMessage);
+ return;
+ }
+ }
+ }
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate);
+
+ certificate.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendChangeCipherSpecMessage()
+ {
+ byte[] message = new byte[]{ 1 };
+ SafeWriteRecord(ContentType.change_cipher_spec, message, 0, message.Length);
+ mRecordStream.SentWriteCipherSpec();
+ }
+
+ protected virtual void SendFinishedMessage()
+ {
+ byte[] verify_data = CreateVerifyData(Context.IsServer);
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.Length);
+
+ message.Write(verify_data, 0, verify_data.Length);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendSupplementalDataMessage(IList supplementalData)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data);
+
+ WriteSupplementalData(message, supplementalData);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual byte[] CreateVerifyData(bool isServer)
+ {
+ TlsContext context = Context;
+ string asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished;
+ byte[] sslSender = isServer ? TlsUtilities.SSL_SERVER : TlsUtilities.SSL_CLIENT;
+ byte[] hash = GetCurrentPrfHash(context, mRecordStream.HandshakeHash, sslSender);
+ return TlsUtilities.CalculateVerifyData(context, asciiLabel, hash);
+ }
+
+ /**
+ * Closes this connection.
+ *
+ * @throws IOException If something goes wrong during closing.
+ */
+ public virtual void Close()
+ {
+ HandleClose(true);
+ }
+
+ protected virtual void HandleClose(bool user_canceled)
+ {
+ if (!mClosed)
+ {
+ if (user_canceled && !mAppDataReady)
+ {
+ RaiseWarning(AlertDescription.user_canceled, "User canceled handshake");
+ }
+ this.FailWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null);
+ }
+ }
+
+ protected internal virtual void Flush()
+ {
+ mRecordStream.Flush();
+ }
+
+ protected internal virtual bool IsClosed
+ {
+ get { return mClosed; }
+ }
+
+ protected virtual short ProcessMaxFragmentLengthExtension(IDictionary clientExtensions, IDictionary serverExtensions,
+ byte alertDescription)
+ {
+ short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions);
+ if (maxFragmentLength >= 0 && !this.mResumedSession)
+ {
+ if (maxFragmentLength != TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions))
+ throw new TlsFatalAlert(alertDescription);
+ }
+ return maxFragmentLength;
+ }
+
+ /**
+ * Make sure the InputStream 'buf' now empty. Fail otherwise.
+ *
+ * @param buf The InputStream to check.
+ * @throws IOException If 'buf' is not empty.
+ */
protected internal static void AssertEmpty(MemoryStream buf)
{
if (buf.Position < buf.Length)
throw new TlsFatalAlert(AlertDescription.decode_error);
}
+ protected internal static byte[] CreateRandomBlock(bool useGmtUnixTime, IRandomGenerator randomGenerator)
+ {
+ byte[] result = new byte[32];
+ randomGenerator.NextBytes(result);
+
+ if (useGmtUnixTime)
+ {
+ TlsUtilities.WriteGmtUnixTime(result, 0);
+ }
+
+ return result;
+ }
+
+ protected internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+ {
+ return TlsUtilities.EncodeOpaque8(renegotiated_connection);
+ }
+
+ protected internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
+ {
+ byte[] pre_master_secret = keyExchange.GeneratePremasterSecret();
+
+ try
+ {
+ context.SecurityParameters.masterSecret = TlsUtilities.CalculateMasterSecret(context, pre_master_secret);
+ }
+ finally
+ {
+ // TODO Is there a way to ensure the data is really overwritten?
+ /*
+ * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the
+ * master_secret has been computed.
+ */
+ if (pre_master_secret != null)
+ {
+ Arrays.Fill(pre_master_secret, (byte)0);
+ }
+ }
+ }
+
+ /**
+ * 'sender' only relevant to SSLv3
+ */
+ protected internal static byte[] GetCurrentPrfHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender)
+ {
+ IDigest d = handshakeHash.ForkPrfHash();
+
+ if (sslSender != null && TlsUtilities.IsSsl(context))
+ {
+ d.BlockUpdate(sslSender, 0, sslSender.Length);
+ }
+
+ return DigestUtilities.DoFinal(d);
+ }
+
protected internal static IDictionary ReadExtensions(MemoryStream input)
{
if (input.Position >= input.Length)
@@ -51,6 +849,27 @@ namespace Org.BouncyCastle.Crypto.Tls
return extensions;
}
+ protected internal static IList ReadSupplementalDataMessage(MemoryStream input)
+ {
+ byte[] supp_data = TlsUtilities.ReadOpaque24(input);
+
+ AssertEmpty(input);
+
+ MemoryStream buf = new MemoryStream(supp_data, false);
+
+ IList supplementalData = Platform.CreateArrayList();
+
+ while (buf.Position < buf.Length)
+ {
+ int supp_data_type = TlsUtilities.ReadUint16(buf);
+ byte[] data = TlsUtilities.ReadOpaque16(buf);
+
+ supplementalData.Add(new SupplementalDataEntry(supp_data_type, data));
+ }
+
+ return supplementalData;
+ }
+
protected internal static void WriteExtensions(Stream output, IDictionary extensions)
{
MemoryStream buf = new MemoryStream();
@@ -68,5 +887,216 @@ namespace Org.BouncyCastle.Crypto.Tls
TlsUtilities.WriteOpaque16(extBytes, output);
}
+
+ protected internal static void WriteSupplementalData(Stream output, IList supplementalData)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ foreach (SupplementalDataEntry entry in supplementalData)
+ {
+ int supp_data_type = entry.DataType;
+ TlsUtilities.CheckUint16(supp_data_type);
+ TlsUtilities.WriteUint16(supp_data_type, buf);
+ TlsUtilities.WriteOpaque16(entry.Data, buf);
+ }
+
+ byte[] supp_data = buf.ToArray();
+
+ TlsUtilities.WriteOpaque24(supp_data, output);
+ }
+
+ protected internal static int GetPrfAlgorithm(TlsContext context, int ciphersuite)
+ {
+ bool isTLSv12 = TlsUtilities.IsTlsV12(context);
+
+ switch (ciphersuite)
+ {
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha256;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha384;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha384;
+ }
+ return PrfAlgorithm.tls_prf_legacy;
+ }
+
+ default:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha256;
+ }
+ return PrfAlgorithm.tls_prf_legacy;
+ }
+ }
+ }
+
+ internal class HandshakeMessage
+ : MemoryStream
+ {
+ internal HandshakeMessage(byte handshakeType)
+ : this(handshakeType, 60)
+ {
+ }
+
+ internal HandshakeMessage(byte handshakeType, int length)
+ : base(length + 4)
+ {
+ TlsUtilities.WriteUint8(handshakeType, this);
+ // Reserve space for length
+ TlsUtilities.WriteUint24(0, this);
+ }
+
+ internal void Write(byte[] data)
+ {
+ Write(data, 0, data.Length);
+ }
+
+ internal void WriteToRecordStream(TlsProtocol protocol)
+ {
+ // Patch actual length back in
+ long length = Length - 4;
+ TlsUtilities.CheckUint24(length);
+ this.Position = 1;
+ TlsUtilities.WriteUint24((int)length, this);
+ protocol.WriteHandshakeMessage(GetBuffer(), 0, (int)Length);
+ this.Close();
+ }
+ }
}
}
diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 72bf8c5cf..6f223467f 100644
--- a/crypto/src/crypto/tls/TlsProtocolHandler.cs
+++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -21,1183 +21,19 @@ using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <remarks>An implementation of all high level protocols in TLS 1.0.</remarks>
+ [Obsolete("Use 'TlsClientProtocol' instead")]
public class TlsProtocolHandler
- : TlsProtocol
+ : TlsClientProtocol
{
- /*
- * Our Connection states
- */
- private const short CS_CLIENT_HELLO_SEND = 1;
- private const short CS_SERVER_HELLO_RECEIVED = 2;
- private const short CS_SERVER_CERTIFICATE_RECEIVED = 3;
- private const short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;
- private const short CS_CERTIFICATE_REQUEST_RECEIVED = 5;
- private const short CS_SERVER_HELLO_DONE_RECEIVED = 6;
- private const short CS_CLIENT_KEY_EXCHANGE_SEND = 7;
- private const short CS_CERTIFICATE_VERIFY_SEND = 8;
- private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 9;
- private const short CS_CLIENT_FINISHED_SEND = 10;
- private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 11;
- private const short CS_DONE = 12;
-
- private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
-
- /*
- * Queues for data from some protocols.
- */
-
- private ByteQueue applicationDataQueue = new ByteQueue();
- private ByteQueue alertQueue = new ByteQueue(2);
- private ByteQueue handshakeQueue = new ByteQueue();
-
- /*
- * The Record Stream we use
- */
- private RecordStream rs;
- private SecureRandom random;
-
- private TlsStream tlsStream = null;
-
- private bool closed = false;
- private bool failedWithError = false;
- private bool appDataReady = false;
- private IDictionary clientExtensions;
-
- private SecurityParameters securityParameters = null;
-
- private TlsClientContextImpl tlsClientContext = null;
- private TlsClient tlsClient = null;
- private int[] offeredCipherSuites = null;
- private byte[] offeredCompressionMethods = null;
- private TlsKeyExchange keyExchange = null;
- private TlsAuthentication authentication = null;
- private CertificateRequest certificateRequest = null;
-
- private short connection_state = 0;
-
- private static SecureRandom CreateSecureRandom()
- {
- /*
- * We use our threaded seed generator to generate a good random seed. If the user
- * has a better random seed, he should use the constructor with a SecureRandom.
- *
- * Hopefully, 20 bytes in fast mode are good enough.
- */
- byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true);
-
- return new SecureRandom(seed);
- }
-
- public TlsProtocolHandler(
- Stream s)
- : this(s, s)
- {
- }
-
- public TlsProtocolHandler(
- Stream s,
- SecureRandom sr)
- : this(s, s, sr)
+ public TlsProtocolHandler(Stream stream, SecureRandom secureRandom)
+ : base(stream, stream, secureRandom)
{
}
/// <remarks>Both streams can be the same object</remarks>
- public TlsProtocolHandler(
- Stream inStr,
- Stream outStr)
- : this(inStr, outStr, CreateSecureRandom())
- {
- }
-
- /// <remarks>Both streams can be the same object</remarks>
- public TlsProtocolHandler(
- Stream inStr,
- Stream outStr,
- SecureRandom sr)
- {
- this.rs = new RecordStream(this, inStr, outStr);
- this.random = sr;
- }
-
- internal void ProcessData(
- byte contentType,
- byte[] buf,
- int offset,
- int len)
- {
- /*
- * Have a look at the protocol type, and add it to the correct queue.
- */
- switch (contentType)
- {
- case ContentType.change_cipher_spec:
- ProcessChangeCipherSpec(buf, offset, len);
- break;
- case ContentType.alert:
- alertQueue.AddData(buf, offset, len);
- ProcessAlert();
- break;
- case ContentType.handshake:
- handshakeQueue.AddData(buf, offset, len);
- ProcessHandshake();
- break;
- case ContentType.application_data:
- if (!appDataReady)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
- applicationDataQueue.AddData(buf, offset, len);
- ProcessApplicationData();
- break;
- default:
- /*
- * Uh, we don't know this protocol.
- *
- * RFC2246 defines on page 13, that we should ignore this.
- */
- break;
- }
- }
-
- private void ProcessHandshake()
- {
- bool read;
- do
- {
- read = false;
-
- /*
- * We need the first 4 bytes, they contain type and length of
- * the message.
- */
- if (handshakeQueue.Available >= 4)
- {
- byte[] beginning = new byte[4];
- handshakeQueue.Read(beginning, 0, 4, 0);
- MemoryStream bis = new MemoryStream(beginning, false);
- byte handshakeType = TlsUtilities.ReadUint8(bis);
- int len = TlsUtilities.ReadUint24(bis);
-
- /*
- * Check if we have enough bytes in the buffer to read
- * the full message.
- */
- if (handshakeQueue.Available >= (len + 4))
- {
- /*
- * Read the message.
- */
- byte[] buf = handshakeQueue.RemoveData(len, 4);
-
- /*
- * RFC 2246 7.4.9. The value handshake_messages includes all
- * handshake messages starting at client hello up to, but not
- * including, this finished message. [..] Note: [Also,] Hello Request
- * messages are omitted from handshake hashes.
- */
- switch (handshakeType)
- {
- case HandshakeType.hello_request:
- case HandshakeType.finished:
- break;
- default:
- rs.UpdateHandshakeData(beginning, 0, 4);
- rs.UpdateHandshakeData(buf, 0, len);
- break;
- }
-
- /*
- * Now, parse the message.
- */
- ProcessHandshakeMessage(handshakeType, buf);
- read = true;
- }
- }
- }
- while (read);
- }
-
- private void ProcessHandshakeMessage(byte handshakeType, byte[] buf)
- {
- MemoryStream inStr = new MemoryStream(buf, false);
-
- /*
- * Check the type.
- */
- switch (handshakeType)
- {
- case HandshakeType.certificate:
- {
- switch (connection_state)
- {
- case CS_SERVER_HELLO_RECEIVED:
- {
- // Parse the Certificate message and send to cipher suite
-
- Certificate serverCertificate = Certificate.Parse(inStr);
-
- AssertEmpty(inStr);
-
- this.keyExchange.ProcessServerCertificate(serverCertificate);
-
- this.authentication = tlsClient.GetAuthentication();
- this.authentication.NotifyServerCertificate(serverCertificate);
-
- break;
- }
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
-
- connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
- break;
- }
- case HandshakeType.finished:
- switch (connection_state)
- {
- case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED:
- /*
- * Read the checksum from the finished message, it has always 12 bytes.
- */
- byte[] serverVerifyData = new byte[12];
- TlsUtilities.ReadFully(serverVerifyData, inStr);
-
- AssertEmpty(inStr);
-
- /*
- * Calculate our own checksum.
- */
- byte[] expectedServerVerifyData = TlsUtilities.PRF(
- securityParameters.masterSecret, "server finished",
- rs.GetCurrentHash(), 12);
-
- /*
- * Compare both checksums.
- */
- if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData))
- {
- /*
- * Wrong checksum in the finished message.
- */
- this.FailWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
- }
-
- connection_state = CS_DONE;
-
- /*
- * We are now ready to receive application data.
- */
- this.appDataReady = true;
- break;
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
- break;
- case HandshakeType.server_hello:
- switch (connection_state)
- {
- case CS_CLIENT_HELLO_SEND:
- /*
- * Read the server hello message
- */
- TlsUtilities.CheckVersion(inStr);
-
- /*
- * Read the server random
- */
- securityParameters.serverRandom = new byte[32];
- TlsUtilities.ReadFully(securityParameters.serverRandom, inStr);
-
- byte[] sessionID = TlsUtilities.ReadOpaque8(inStr);
- if (sessionID.Length > 32)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
- }
-
- this.tlsClient.NotifySessionID(sessionID);
-
- /*
- * Find out which CipherSuite the server has chosen and check that
- * it was one of the offered ones.
- */
- int selectedCipherSuite = TlsUtilities.ReadUint16(inStr);
- if (!Arrays.Contains(offeredCipherSuites, selectedCipherSuite)
- || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
- }
-
- this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
-
- /*
- * Find out which CompressionMethod the server has chosen and check that
- * it was one of the offered ones.
- */
- byte selectedCompressionMethod = TlsUtilities.ReadUint8(inStr);
- if (!Arrays.Contains(offeredCompressionMethods, selectedCompressionMethod))
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
- }
-
- this.tlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
-
- /*
- * RFC3546 2.2 The extended server hello message format MAY be
- * sent in place of the server hello message when the client has
- * requested extended functionality via the extended client hello
- * message specified in Section 2.1.
- * ...
- * Note that the extended server hello message is only sent in response
- * to an extended client hello message. This prevents the possibility
- * that the extended server hello message could "break" existing TLS 1.0
- * clients.
- */
-
- /*
- * TODO RFC 3546 2.3
- * If [...] the older session is resumed, then the server MUST ignore
- * extensions appearing in the client hello, and send a server hello
- * containing no extensions.
- */
-
- // Int32 -> byte[]
- IDictionary serverExtensions = ReadExtensions(inStr);
-
- /*
- * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
- * extended client hello message.
- *
- * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
- * Hello is always allowed.
- */
- if (serverExtensions != null)
- {
- foreach (int extType in serverExtensions.Keys)
- {
- /*
- * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a
- * ClientHello containing only the SCSV is an explicit exception to the prohibition
- * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is
- * only allowed because the client is signaling its willingness to receive the
- * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
- */
- if (ExtensionType.renegotiation_info == extType)
- continue;
-
- // 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[.]
- // */
- //if (this.resumedSession)
- //{
- // // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats
- // // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats
- // // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats
- // // throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- //}
-
- /*
- * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
- * same extension type appeared in the corresponding ClientHello. If a client
- * receives an extension type in ServerHello that it did not request in the
- * associated ClientHello, it MUST abort the handshake with an unsupported_extension
- * fatal alert.
- */
- if (null == TlsUtilities.GetExtensionData(clientExtensions, extType))
- throw new TlsFatalAlert(AlertDescription.unsupported_extension);
- }
- }
- else
- {
- // TODO Don't need this eventually...
- serverExtensions = Platform.CreateHashtable();
- }
-
- /*
- * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it
- * includes the "renegotiation_info" extension:
- */
- {
- bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info);
-
- /*
- * If the extension is present, set the secure_renegotiation flag
- * to TRUE. The client MUST then verify that the length of the
- * "renegotiated_connection" field is zero, and if it is not, MUST
- * abort the handshake (by sending a fatal handshake_failure
- * alert).
- */
- if (secure_negotiation)
- {
- byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info];
-
- if (!Arrays.ConstantTimeAreEqual(renegExtValue,
- CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
- }
-
- tlsClient.NotifySecureRenegotiation(secure_negotiation);
- }
-
- if (clientExtensions != null)
- {
- tlsClient.ProcessServerExtensions(serverExtensions);
- }
-
- this.keyExchange = tlsClient.GetKeyExchange();
-
- connection_state = CS_SERVER_HELLO_RECEIVED;
- break;
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
- break;
- case HandshakeType.server_hello_done:
- switch (connection_state)
- {
- case CS_SERVER_HELLO_RECEIVED:
- case CS_SERVER_CERTIFICATE_RECEIVED:
- case CS_SERVER_KEY_EXCHANGE_RECEIVED:
- case CS_CERTIFICATE_REQUEST_RECEIVED:
-
- // NB: Original code used case label fall-through
-
- if (connection_state == CS_SERVER_HELLO_RECEIVED)
- {
- // There was no server certificate message; check it's OK
- this.keyExchange.SkipServerCertificate();
- this.authentication = null;
-
- // There was no server key exchange message; check it's OK
- this.keyExchange.SkipServerKeyExchange();
- }
- else if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
- {
- // There was no server key exchange message; check it's OK
- this.keyExchange.SkipServerKeyExchange();
- }
-
- AssertEmpty(inStr);
-
- connection_state = CS_SERVER_HELLO_DONE_RECEIVED;
-
- TlsCredentials clientCreds = null;
- if (certificateRequest == null)
- {
- this.keyExchange.SkipClientCredentials();
- }
- else
- {
- clientCreds = this.authentication.GetClientCredentials(certificateRequest);
-
- Certificate clientCert;
- if (clientCreds == null)
- {
- this.keyExchange.SkipClientCredentials();
- clientCert = Certificate.EmptyChain;
- }
- else
- {
- this.keyExchange.ProcessClientCredentials(clientCreds);
- clientCert = clientCreds.Certificate;
- }
-
- SendClientCertificate(clientCert);
- }
-
- /*
- * Send the client key exchange message, depending on the key
- * exchange we are using in our CipherSuite.
- */
- SendClientKeyExchange();
-
- connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;
-
- if (clientCreds != null && clientCreds is TlsSignerCredentials)
- {
- TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
- byte[] md5andsha1 = rs.GetCurrentHash();
- byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature(
- md5andsha1);
- SendCertificateVerify(clientCertificateSignature);
-
- connection_state = CS_CERTIFICATE_VERIFY_SEND;
- }
-
- /*
- * Now, we send change cipher state
- */
- byte[] cmessage = new byte[1];
- cmessage[0] = 1;
- rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length);
-
- connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;
-
- /*
- * Calculate the master_secret
- */
- byte[] pms = this.keyExchange.GeneratePremasterSecret();
-
- securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret",
- TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom),
- 48);
-
- // TODO Is there a way to ensure the data is really overwritten?
- /*
- * RFC 2246 8.1. The pre_master_secret should be deleted from
- * memory once the master_secret has been computed.
- */
- Array.Clear(pms, 0, pms.Length);
-
- /*
- * Initialize our cipher suite
- */
- rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher());
-
- /*
- * Send our finished message.
- */
- byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret,
- "client finished", rs.GetCurrentHash(), 12);
-
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos);
- TlsUtilities.WriteOpaque24(clientVerifyData, bos);
- byte[] message = bos.ToArray();
-
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
-
- this.connection_state = CS_CLIENT_FINISHED_SEND;
- break;
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- break;
- }
- break;
- case HandshakeType.server_key_exchange:
- {
- switch (connection_state)
- {
- case CS_SERVER_HELLO_RECEIVED:
- case CS_SERVER_CERTIFICATE_RECEIVED:
- {
- // NB: Original code used case label fall-through
- if (connection_state == CS_SERVER_HELLO_RECEIVED)
- {
- // There was no server certificate message; check it's OK
- this.keyExchange.SkipServerCertificate();
- this.authentication = null;
- }
-
- this.keyExchange.ProcessServerKeyExchange(inStr);
-
- AssertEmpty(inStr);
- break;
- }
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
-
- this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
- break;
- }
- case HandshakeType.certificate_request:
- switch (connection_state)
- {
- case CS_SERVER_CERTIFICATE_RECEIVED:
- case CS_SERVER_KEY_EXCHANGE_RECEIVED:
- {
- // NB: Original code used case label fall-through
- if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
- {
- // There was no server key exchange message; check it's OK
- this.keyExchange.SkipServerKeyExchange();
- }
-
- if (this.authentication == null)
- {
- /*
- * RFC 2246 7.4.4. It is a fatal handshake_failure alert
- * for an anonymous server to request client identification.
- */
- this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
-
- this.certificateRequest = CertificateRequest.Parse(//getContext(),
- inStr);
-
- AssertEmpty(inStr);
-
- this.keyExchange.ValidateCertificateRequest(this.certificateRequest);
-
- break;
- }
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
-
- this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
- break;
- case HandshakeType.hello_request:
- /*
- * RFC 2246 7.4.1.1 Hello request
- * This message will be ignored by the client if the client is currently
- * negotiating a session. This message may be ignored by the client if it
- * does not wish to renegotiate a session, or the client may, if it wishes,
- * respond with a no_renegotiation alert.
- */
- if (connection_state == CS_DONE)
- {
- // Renegotiation not supported yet
- SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation);
- }
- break;
- case HandshakeType.client_key_exchange:
- case HandshakeType.certificate_verify:
- case HandshakeType.client_hello:
- default:
- // We do not support this!
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
- }
-
- private void ProcessApplicationData()
- {
- /*
- * There is nothing we need to do here.
- *
- * This function could be used for callbacks when application
- * data arrives in the future.
- */
- }
-
- private void ProcessAlert()
- {
- while (alertQueue.Available >= 2)
- {
- /*
- * An alert is always 2 bytes. Read the alert.
- */
- byte[] tmp = alertQueue.RemoveData(2, 0);
- byte level = tmp[0];
- byte description = tmp[1];
- if (level == (byte)AlertLevel.fatal)
- {
- this.failedWithError = true;
- this.closed = true;
- /*
- * Now try to Close the stream, ignore errors.
- */
- try
- {
- rs.Close();
- }
- catch (Exception)
- {
- }
- throw new IOException(TLS_ERROR_MESSAGE);
- }
- else
- {
- if (description == (byte)AlertDescription.close_notify)
- {
- HandleClose(false);
- }
-
- /*
- * If it is just a warning, we continue.
- */
- }
- }
- }
-
- /**
- * This method is called, when a change cipher spec message is received.
- *
- * @throws IOException If the message has an invalid content or the
- * handshake is not in the correct state.
- */
- private void ProcessChangeCipherSpec(byte[] buf, int off, int len)
- {
- for (int i = 0; i < len; ++i)
- {
- if (buf[off + i] != 1)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.decode_error);
- }
-
- /*
- * Check if we are in the correct connection state.
- */
- if (this.connection_state != CS_CLIENT_FINISHED_SEND)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
-
- rs.ServerClientSpecReceived();
-
- this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
- }
- }
-
- private void SendClientCertificate(Certificate clientCert)
- {
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.certificate, bos);
-
- // Reserve space for length
- TlsUtilities.WriteUint24(0, bos);
-
- clientCert.Encode(bos);
- byte[] message = bos.ToArray();
-
- // Patch actual length back in
- TlsUtilities.WriteUint24(message.Length - 4, message, 1);
-
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
- }
-
- private void SendClientKeyExchange()
- {
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.client_key_exchange, bos);
-
- // Reserve space for length
- TlsUtilities.WriteUint24(0, bos);
-
- this.keyExchange.GenerateClientKeyExchange(bos);
- byte[] message = bos.ToArray();
-
- // Patch actual length back in
- TlsUtilities.WriteUint24(message.Length - 4, message, 1);
-
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
- }
-
- private void SendCertificateVerify(byte[] data)
- {
- /*
- * Send signature of handshake messages so far to prove we are the owner of
- * the cert See RFC 2246 sections 4.7, 7.4.3 and 7.4.8
- */
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.certificate_verify, bos);
- TlsUtilities.WriteUint24(data.Length + 2, bos);
- TlsUtilities.WriteOpaque16(data, bos);
- byte[] message = bos.ToArray();
-
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
- }
-
- /// <summary>Connects to the remote system.</summary>
- /// <param name="verifyer">Will be used when a certificate is received to verify
- /// that this certificate is accepted by the client.</param>
- /// <exception cref="IOException">If handshake was not successful</exception>
- [Obsolete("Use version taking TlsClient")]
- public virtual void Connect(
- ICertificateVerifyer verifyer)
- {
- this.Connect(new LegacyTlsClient(verifyer));
- }
-
- public virtual void Connect(TlsClient tlsClient)
- {
- if (tlsClient == null)
- throw new ArgumentNullException("tlsClient");
- if (this.tlsClient != null)
- throw new InvalidOperationException("Connect can only be called once");
-
- /*
- * Send Client hello
- *
- * First, generate some random data.
- */
- this.securityParameters = new SecurityParameters();
- this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), random,
- ExporterLabel.client_random);
-
- this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
- this.tlsClient = tlsClient;
- this.tlsClient.Init(tlsClientContext);
-
- MemoryStream outStr = new MemoryStream();
- TlsUtilities.WriteVersion(outStr);
- outStr.Write(securityParameters.clientRandom, 0, 32);
-
- /*
- * Length of Session id
- */
- TlsUtilities.WriteUint8(0, outStr);
-
- this.offeredCipherSuites = this.tlsClient.GetCipherSuites();
-
- // Int32 -> byte[]
- this.clientExtensions = this.tlsClient.GetClientExtensions();
-
- // Cipher Suites (and SCSV)
- {
- /*
- * RFC 5746 3.4.
- * The client MUST include either an empty "renegotiation_info"
- * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
- * cipher suite value in the ClientHello. Including both is NOT
- * RECOMMENDED.
- */
- bool noRenegExt = clientExtensions == null
- || !clientExtensions.Contains(ExtensionType.renegotiation_info);
-
- int count = offeredCipherSuites.Length;
- if (noRenegExt)
- {
- // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- ++count;
- }
-
- TlsUtilities.WriteUint16(2 * count, outStr);
-
- for (int i = 0; i < offeredCipherSuites.Length; ++i)
- {
- TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
- }
-
- if (noRenegExt)
- {
- TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
- }
- }
-
- /*
- * Compression methods, just the null method.
- */
- this.offeredCompressionMethods = tlsClient.GetCompressionMethods();
-
- {
- TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
- for (int i = 0; i < offeredCompressionMethods.Length; ++i)
- {
- TlsUtilities.WriteUint8(offeredCompressionMethods[i], outStr);
- }
- }
-
- if (clientExtensions != null)
- {
- WriteExtensions(outStr, clientExtensions);
- }
-
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
- TlsUtilities.WriteUint24((int)outStr.Length, bos);
- byte[] outBytes = outStr.ToArray();
- bos.Write(outBytes, 0, outBytes.Length);
- byte[] message = bos.ToArray();
- SafeWriteMessage(ContentType.handshake, message, 0, message.Length);
- connection_state = CS_CLIENT_HELLO_SEND;
-
- /*
- * We will now read data, until we have completed the handshake.
- */
- while (connection_state != CS_DONE)
- {
- SafeReadData();
- }
-
- this.tlsStream = new TlsStream(this);
- }
-
- /**
- * Read data from the network. The method will return immediately, if there is
- * still some data left in the buffer, or block until some application
- * data has been read from the network.
- *
- * @param buf The buffer where the data will be copied to.
- * @param offset The position where the data will be placed in the buffer.
- * @param len The maximum number of bytes to read.
- * @return The number of bytes read.
- * @throws IOException If something goes wrong during reading data.
- */
- internal int ReadApplicationData(byte[] buf, int offset, int len)
- {
- while (applicationDataQueue.Available == 0)
- {
- if (this.closed)
- {
- /*
- * We need to read some data.
- */
- if (this.failedWithError)
- {
- /*
- * Something went terribly wrong, we should throw an IOException
- */
- throw new IOException(TLS_ERROR_MESSAGE);
- }
-
- /*
- * Connection has been closed, there is no more data to read.
- */
- return 0;
- }
-
- SafeReadData();
- }
- len = System.Math.Min(len, applicationDataQueue.Available);
- applicationDataQueue.RemoveData(buf, offset, len, 0);
- return len;
- }
-
- private void SafeReadData()
- {
- try
- {
- rs.ReadData();
- }
- catch (TlsFatalAlert e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, e.AlertDescription);
- }
- throw e;
- }
- catch (IOException e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- catch (Exception e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- }
-
- private void SafeWriteMessage(byte type, byte[] buf, int offset, int len)
- {
- try
- {
- rs.WriteMessage(type, buf, offset, len);
- }
- catch (TlsFatalAlert e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, e.AlertDescription);
- }
- throw e;
- }
- catch (IOException e)
- {
- if (!closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- catch (Exception e)
- {
- if (!closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- }
-
- /**
- * Send some application data to the remote system.
- * <p/>
- * The method will handle fragmentation internally.
- *
- * @param buf The buffer with the data.
- * @param offset The position in the buffer where the data is placed.
- * @param len The length of the data.
- * @throws IOException If something goes wrong during sending.
- */
- internal void WriteData(byte[] buf, int offset, int len)
- {
- if (this.closed)
- {
- if (this.failedWithError)
- throw new IOException(TLS_ERROR_MESSAGE);
-
- throw new IOException("Sorry, connection has been closed, you cannot write more data");
- }
-
- while (len > 0)
- {
- /*
- * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
- * potentially useful as a traffic analysis countermeasure.
- *
- * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
- */
-
- //if (this.splitApplicationDataRecords)
- {
- /*
- * Protect against known IV attack!
- *
- * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
- */
- SafeWriteMessage(ContentType.application_data, buf, offset, 1);
- ++offset;
- --len;
- }
-
- if (len > 0)
- {
- // Fragment data according to the current fragment limit.
- //int toWrite = System.Math.Min(len, recordStream.GetPlaintextLimit());
- int toWrite = System.Math.Min(len, 1 << 14);
- SafeWriteMessage(ContentType.application_data, buf, offset, toWrite);
- offset += toWrite;
- len -= toWrite;
- }
- }
- }
-
- /// <summary>A Stream which can be used to send data.</summary>
- [Obsolete("Use 'Stream' property instead")]
- public virtual Stream OutputStream
- {
- get { return this.tlsStream; }
- }
-
- /// <summary>A Stream which can be used to read data.</summary>
- [Obsolete("Use 'Stream' property instead")]
- public virtual Stream InputStream
- {
- get { return this.tlsStream; }
- }
-
- /// <summary>The secure bidirectional stream for this connection</summary>
- public virtual Stream Stream
- {
- get { return this.tlsStream; }
- }
-
- /**
- * Terminate this connection with an alert.
- * <p/>
- * Can be used for normal closure too.
- *
- * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning.
- * @param alertDescription The exact alert message.
- * @throws IOException If alert was fatal.
- */
- private void FailWithError(byte alertLevel, byte alertDescription)
- {
- /*
- * Check if the connection is still open.
- */
- if (!closed)
- {
- /*
- * Prepare the message
- */
- this.closed = true;
-
- if (alertLevel == AlertLevel.fatal)
- {
- /*
- * This is a fatal message.
- */
- this.failedWithError = true;
- }
- SendAlert(alertLevel, alertDescription);
- rs.Close();
- if (alertLevel == AlertLevel.fatal)
- {
- throw new IOException(TLS_ERROR_MESSAGE);
- }
- }
- else
- {
- throw new IOException(TLS_ERROR_MESSAGE);
- }
- }
-
- internal void SendAlert(byte alertLevel, byte alertDescription)
- {
- byte[] error = new byte[] { alertLevel, alertDescription };
-
- rs.WriteMessage(ContentType.alert, error, 0, 2);
- }
-
- /// <summary>Closes this connection</summary>
- /// <exception cref="IOException">If something goes wrong during closing.</exception>
- public virtual void Close()
- {
- HandleClose(true);
- }
-
- protected virtual void HandleClose(bool user_canceled)
- {
- if (!closed)
- {
- if (user_canceled && !appDataReady)
- {
- SendAlert(AlertLevel.warning, AlertDescription.user_canceled);
- }
- this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
- }
- }
-
- protected static byte[] CreateRandomBlock(bool useGMTUnixTime, SecureRandom random, string asciiLabel)
- {
- /*
- * We use the TLS 1.0 PRF on the SecureRandom output, to guard against RNGs where the raw
- * output could be used to recover the internal state.
- */
- byte[] secret = new byte[32];
- random.NextBytes(secret);
-
- byte[] seed = new byte[8];
- // TODO Use high-resolution timer
- TlsUtilities.WriteUint64(DateTimeUtilities.CurrentUnixMs(), seed, 0);
-
- byte[] result = TlsUtilities.PRF(secret, asciiLabel, seed, 32);
-
- if (useGMTUnixTime)
- {
- TlsUtilities.WriteGmtUnixTime(result, 0);
- }
-
- return result;
- }
-
- internal void Flush()
- {
- rs.Flush();
- }
-
- internal bool IsClosed
- {
- get { return closed; }
- }
-
- private static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+ public TlsProtocolHandler(Stream input, Stream output, SecureRandom secureRandom)
+ : base(input, output, secureRandom)
{
- MemoryStream buf = new MemoryStream();
- TlsUtilities.WriteOpaque8(renegotiated_connection, buf);
- return buf.ToArray();
}
}
}
diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
index 9961fc9d1..cd13e3438 100644
--- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -1,172 +1,211 @@
using System;
+using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Tls
{
- internal class TlsPskKeyExchange
- : TlsKeyExchange
+ /// <summary>(D)TLS PSK key exchange (RFC 4279).</summary>
+ public class TlsPskKeyExchange
+ : AbstractTlsKeyExchange
{
- protected TlsClientContext context;
- protected int keyExchange;
- protected TlsPskIdentity pskIdentity;
+ protected TlsPskIdentity mPskIdentity;
+ protected DHParameters mDHParameters;
+ protected int[] mNamedCurves;
+ protected byte[] mClientECPointFormats, mServerECPointFormats;
- protected byte[] psk_identity_hint = null;
+ protected byte[] mPskIdentityHint = null;
- protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
- protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+ protected DHPrivateKeyParameters mDHAgreePrivateKey = null;
+ protected DHPublicKeyParameters mDHAgreePublicKey = null;
- protected AsymmetricKeyParameter serverPublicKey = null;
- protected RsaKeyParameters rsaServerPublicKey = null;
- protected byte[] premasterSecret;
+ protected AsymmetricKeyParameter mServerPublicKey = null;
+ protected RsaKeyParameters mRsaServerPublicKey = null;
+ protected TlsEncryptionCredentials mServerCredentials = null;
+ protected byte[] mPremasterSecret;
- internal TlsPskKeyExchange(TlsClientContext context, int keyExchange,
- TlsPskIdentity pskIdentity)
+ public TlsPskKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsPskIdentity pskIdentity,
+ DHParameters dhParameters, int[] namedCurves, byte[] clientECPointFormats, byte[] serverECPointFormats)
+ : base(keyExchange, supportedSignatureAlgorithms)
{
switch (keyExchange)
{
- case KeyExchangeAlgorithm.PSK:
- case KeyExchangeAlgorithm.RSA_PSK:
- case KeyExchangeAlgorithm.DHE_PSK:
- break;
- default:
- throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ break;
+ default:
+ throw new InvalidOperationException("unsupported key exchange algorithm");
}
- this.context = context;
- this.keyExchange = keyExchange;
- this.pskIdentity = pskIdentity;
+ this.mPskIdentity = pskIdentity;
+ this.mDHParameters = dhParameters;
+ this.mNamedCurves = namedCurves;
+ this.mClientECPointFormats = clientECPointFormats;
+ this.mServerECPointFormats = serverECPointFormats;
}
- public virtual void SkipServerCertificate()
+ public override void SkipServerCredentials()
{
- if (keyExchange == KeyExchangeAlgorithm.RSA_PSK)
- {
+ if (mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
}
- public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
{
- if (keyExchange != KeyExchangeAlgorithm.RSA_PSK)
+ if (!(serverCredentials is TlsEncryptionCredentials))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ ProcessServerCertificate(serverCredentials.Certificate);
+
+ this.mServerCredentials = (TlsEncryptionCredentials)serverCredentials;
+ }
+
+ public override byte[] GenerateServerKeyExchange()
+ {
+ // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys
+ this.mPskIdentityHint = null;
+
+ if (this.mPskIdentityHint == null && !RequiresServerKeyExchange)
+ return null;
+
+ MemoryStream buf = new MemoryStream();
+
+ if (this.mPskIdentityHint == null)
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ TlsUtilities.WriteOpaque16(TlsUtilities.EmptyBytes, buf);
+ }
+ else
+ {
+ TlsUtilities.WriteOpaque16(this.mPskIdentityHint, buf);
}
+ if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ if (this.mDHParameters == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+ this.mDHParameters, buf);
+ }
+ else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ // TODO[RFC 5489]
+ }
+
+ return buf.ToArray();
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ if (mKeyExchange != KeyExchangeAlgorithm.RSA_PSK)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
- SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
try
{
- this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
}
- // catch (RuntimeException)
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
// Sanity check the PublicKeyFactory
- if (this.serverPublicKey.IsPrivate)
- {
+ if (this.mServerPublicKey.IsPrivate)
throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey);
+ this.mRsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.mServerPublicKey);
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
- // TODO
- /*
- * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
- * signing algorithm for the certificate must be the same as the algorithm for the
- * certificate key."
- */
+ base.ProcessServerCertificate(serverCertificate);
}
- public virtual void SkipServerKeyExchange()
+ public override bool RequiresServerKeyExchange
{
- if (keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ get
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ switch (mKeyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ return true;
+ default:
+ return false;
+ }
}
-
- this.psk_identity_hint = TlsUtilities.EmptyBytes;
}
- public virtual void ProcessServerKeyExchange(Stream input)
+ public override void ProcessServerKeyExchange(Stream input)
{
- this.psk_identity_hint = TlsUtilities.ReadOpaque16(input);
+ this.mPskIdentityHint = TlsUtilities.ReadOpaque16(input);
- if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
{
- byte[] pBytes = TlsUtilities.ReadOpaque16(input);
- byte[] gBytes = TlsUtilities.ReadOpaque16(input);
- byte[] YsBytes = TlsUtilities.ReadOpaque16(input);
+ ServerDHParams serverDHParams = ServerDHParams.Parse(input);
- BigInteger p = new BigInteger(1, pBytes);
- BigInteger g = new BigInteger(1, gBytes);
- BigInteger Ys = new BigInteger(1, YsBytes);
-
- this.dhAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(
- new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(serverDHParams.PublicKey);
}
- else if (this.psk_identity_hint.Length == 0)
+ else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- // TODO Should we enforce that this message should have been skipped if hint is empty?
- //throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ // TODO[RFC 5489]
}
}
- public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public virtual void SkipClientCredentials()
- {
- // OK
- }
-
- public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- public virtual void GenerateClientKeyExchange(Stream output)
+ public override void GenerateClientKeyExchange(Stream output)
{
- if (psk_identity_hint == null || psk_identity_hint.Length == 0)
+ if (mPskIdentityHint == null)
{
- pskIdentity.SkipIdentityHint();
+ mPskIdentity.SkipIdentityHint();
}
else
{
- pskIdentity.NotifyIdentityHint(psk_identity_hint);
+ mPskIdentity.NotifyIdentityHint(mPskIdentityHint);
}
- byte[] psk_identity = pskIdentity.GetPskIdentity();
+ byte[] psk_identity = mPskIdentity.GetPskIdentity();
TlsUtilities.WriteOpaque16(psk_identity, output);
- if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+ mDHAgreePublicKey.Parameters, output);
+ }
+ else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
- context.SecureRandom, this.rsaServerPublicKey, output);
+ // TODO[RFC 5489]
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
- else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
{
- this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
- context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output);
+ this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context,
+ this.mRsaServerPublicKey, output);
}
}
- public virtual byte[] GeneratePremasterSecret()
+ public override byte[] GeneratePremasterSecret()
{
- byte[] psk = pskIdentity.GetPsk();
+ byte[] psk = mPskIdentity.GetPsk();
byte[] other_secret = GenerateOtherSecret(psk.Length);
MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length);
@@ -177,14 +216,25 @@ namespace Org.BouncyCastle.Crypto.Tls
protected virtual byte[] GenerateOtherSecret(int pskLength)
{
- if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ if (mDHAgreePrivateKey != null)
+ {
+ return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreePublicKey, mDHAgreePrivateKey);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- return TlsDHUtilities.CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ // TODO[RFC 5489]
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
- if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
{
- return this.premasterSecret;
+ return this.mPremasterSecret;
}
return new byte[pskLength];
@@ -193,12 +243,10 @@ namespace Org.BouncyCastle.Crypto.Tls
protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key)
{
// TODO What is the minimum bit length required?
- // key.Modulus.BitLength;
+ // key.Modulus.BitLength;
if (!key.Exponent.IsProbablePrime(2))
- {
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
return key;
}
diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index aad482316..3a0a49154 100644
--- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -1,163 +1,138 @@
using System;
+using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <summary>
- /// TLS 1.0 RSA key exchange.
- /// </summary>
- internal class TlsRsaKeyExchange
- : TlsKeyExchange
+ /// <summary>(D)TLS and SSLv3 RSA key exchange.</summary>
+ public class TlsRsaKeyExchange
+ : AbstractTlsKeyExchange
{
- protected TlsClientContext context;
-
protected AsymmetricKeyParameter serverPublicKey = null;
protected RsaKeyParameters rsaServerPublicKey = null;
+ protected TlsEncryptionCredentials serverCredentials = null;
+
protected byte[] premasterSecret;
- internal TlsRsaKeyExchange(TlsClientContext context)
+ public TlsRsaKeyExchange(IList supportedSignatureAlgorithms)
+ : base(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms)
{
- this.context = context;
}
- public virtual void SkipServerCertificate()
+ public override void SkipServerCredentials()
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+ {
+ if (!(serverCredentials is TlsEncryptionCredentials))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ ProcessServerCertificate(serverCredentials.Certificate);
+
+ this.serverCredentials = (TlsEncryptionCredentials)serverCredentials;
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
{
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
- SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
try
{
this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
}
-// catch (RuntimeException)
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
// Sanity check the PublicKeyFactory
if (this.serverPublicKey.IsPrivate)
- {
throw new TlsFatalAlert(AlertDescription.internal_error);
- }
this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey);
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
- // TODO
- /*
- * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
- * signing algorithm for the certificate must be the same as the algorithm for the
- * certificate key."
- */
+ base.ProcessServerCertificate(serverCertificate);
}
- public virtual void SkipServerKeyExchange()
- {
- // OK
- }
-
- public virtual void ProcessServerKeyExchange(Stream input)
- {
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
-
- public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
{
byte[] types = certificateRequest.CertificateTypes;
- foreach (byte type in types)
+ for (int i = 0; i < types.Length; ++i)
{
- switch (type)
+ switch (types[i])
{
- case ClientCertificateType.rsa_sign:
- case ClientCertificateType.dss_sign:
- case ClientCertificateType.ecdsa_sign:
- break;
- default:
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
}
- public virtual void SkipClientCredentials()
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
{
- // OK
+ if (!(clientCredentials is TlsSignerCredentials))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
- public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ public override void GenerateClientKeyExchange(Stream output)
{
- if (!(clientCredentials is TlsSignerCredentials))
- {
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
+ this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, rsaServerPublicKey, output);
}
-
- public virtual void GenerateClientKeyExchange(Stream output)
+
+ public override void ProcessClientKeyExchange(Stream input)
{
- this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
- context.SecureRandom, this.rsaServerPublicKey, output);
+ byte[] encryptedPreMasterSecret;
+ if (TlsUtilities.IsSsl(context))
+ {
+ // TODO Do any SSLv3 clients actually include the length?
+ encryptedPreMasterSecret = Streams.ReadAll(input);
+ }
+ else
+ {
+ encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input);
+ }
+
+ this.premasterSecret = serverCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
}
- public virtual byte[] GeneratePremasterSecret()
+ public override byte[] GeneratePremasterSecret()
{
+ if (this.premasterSecret == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
byte[] tmp = this.premasterSecret;
this.premasterSecret = null;
return tmp;
}
- // Would be needed to process RSA_EXPORT server key exchange
-// protected virtual void ProcessRsaServerKeyExchange(Stream input, ISigner signer)
-// {
-// Stream sigIn = input;
-// if (signer != null)
-// {
-// sigIn = new SignerStream(input, signer, null);
-// }
-//
-// byte[] modulusBytes = TlsUtilities.ReadOpaque16(sigIn);
-// byte[] exponentBytes = TlsUtilities.ReadOpaque16(sigIn);
-//
-// if (signer != null)
-// {
-// byte[] sigByte = TlsUtilities.ReadOpaque16(input);
-//
-// if (!signer.VerifySignature(sigByte))
-// {
-// handler.FailWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
-// }
-// }
-//
-// BigInteger modulus = new BigInteger(1, modulusBytes);
-// BigInteger exponent = new BigInteger(1, exponentBytes);
-//
-// this.rsaServerPublicKey = ValidateRSAPublicKey(new RsaKeyParameters(false, modulus, exponent));
-// }
-
protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key)
{
// TODO What is the minimum bit length required?
-// key.Modulus.BitLength;
+ // key.Modulus.BitLength;
if (!key.Exponent.IsProbablePrime(2))
- {
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
return key;
}
diff --git a/crypto/src/crypto/tls/TlsRsaSigner.cs b/crypto/src/crypto/tls/TlsRsaSigner.cs
index ce18ef5e1..6da1c5e9b 100644
--- a/crypto/src/crypto/tls/TlsRsaSigner.cs
+++ b/crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -10,50 +10,92 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- internal class TlsRsaSigner
- : TlsSigner
+ public class TlsRsaSigner
+ : AbstractTlsSigner
{
- public virtual byte[] GenerateRawSignature(SecureRandom random,
- AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+ AsymmetricKeyParameter privateKey, byte[] hash)
{
- IAsymmetricBlockCipher engine = CreateRsaImpl();
- engine.Init(true, new ParametersWithRandom(privateKey, random));
- return engine.ProcessBlock(md5AndSha1, 0, md5AndSha1.Length);
+ ISigner signer = MakeSigner(algorithm, true, true,
+ new ParametersWithRandom(privateKey, this.mContext.SecureRandom));
+ signer.BlockUpdate(hash, 0, hash.Length);
+ return signer.GenerateSignature();
}
- public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey,
- byte[] md5AndSha1)
+ public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+ AsymmetricKeyParameter publicKey, byte[] hash)
{
- IAsymmetricBlockCipher engine = CreateRsaImpl();
- engine.Init(false, publicKey);
- byte[] signed = engine.ProcessBlock(sigBytes, 0, sigBytes.Length);
- return Arrays.ConstantTimeAreEqual(signed, md5AndSha1);
+ ISigner signer = MakeSigner(algorithm, true, false, publicKey);
+ signer.BlockUpdate(hash, 0, hash.Length);
+ return signer.VerifySignature(sigBytes);
}
- public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+ public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
{
- return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random));
+ return MakeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.mContext.SecureRandom));
}
- public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+ public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
{
- return MakeSigner(new CombinedHash(), false, publicKey);
+ return MakeSigner(algorithm, false, false, publicKey);
}
- public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+ public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
{
return publicKey is RsaKeyParameters && !publicKey.IsPrivate;
}
- protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+ protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning,
+ ICipherParameters cp)
{
- ISigner s = new GenericSigner(CreateRsaImpl(), d);
+ if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext))
+ throw new InvalidOperationException();
+ if (algorithm != null && algorithm.Signature != SignatureAlgorithm.rsa)
+ throw new InvalidOperationException();
+
+ IDigest d;
+ if (raw)
+ {
+ d = new NullDigest();
+ }
+ else if (algorithm == null)
+ {
+ d = new CombinedHash();
+ }
+ else
+ {
+ d = TlsUtilities.CreateHash(algorithm.Hash);
+ }
+
+ ISigner s;
+ if (algorithm != null)
+ {
+ /*
+ * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated
+ * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1].
+ */
+ s = new RsaDigestSigner(d, TlsUtilities.GetOidForHashAlgorithm(algorithm.Hash));
+ }
+ else
+ {
+ /*
+ * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme
+ * that did not include a DigestInfo encoding.
+ */
+ s = new GenericSigner(CreateRsaImpl(), d);
+ }
s.Init(forSigning, cp);
return s;
}
protected virtual IAsymmetricBlockCipher CreateRsaImpl()
{
+ /*
+ * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks
+ * on TLS are possible, at least when the client and server are on the same LAN.
+ * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other
+ * anti-timing technique, as described in [TIMING].
+ */
return new Pkcs1Encoding(new RsaBlindedEngine());
}
}
diff --git a/crypto/src/crypto/tls/TlsRsaUtilities.cs b/crypto/src/crypto/tls/TlsRsaUtilities.cs
index 4450ba452..0e42c1733 100644
--- a/crypto/src/crypto/tls/TlsRsaUtilities.cs
+++ b/crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -5,38 +5,128 @@ using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- public abstract class TlsRsaUtilities
- {
- public static byte[] GenerateEncryptedPreMasterSecret(SecureRandom random,
- RsaKeyParameters rsaServerPublicKey, Stream output)
- {
- /*
- * Choose a PremasterSecret and send it encrypted to the server
- */
- byte[] premasterSecret = new byte[48];
- random.NextBytes(premasterSecret);
- TlsUtilities.WriteVersion(premasterSecret, 0);
-
- Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());
- encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, random));
-
- try
- {
- byte[] keData = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length);
- TlsUtilities.WriteOpaque16(keData, output);
- }
- catch (InvalidCipherTextException)
- {
- /*
- * This should never happen, only during decryption.
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
-
- return premasterSecret;
- }
- }
+ public abstract class TlsRsaUtilities
+ {
+ /// <exception cref="IOException"></exception>
+ public static byte[] GenerateEncryptedPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPublicKey,
+ Stream output)
+ {
+ /*
+ * Choose a PremasterSecret and send it encrypted to the server
+ */
+ byte[] premasterSecret = new byte[48];
+ context.SecureRandom.NextBytes(premasterSecret);
+ TlsUtilities.WriteVersion(context.ClientVersion, premasterSecret, 0);
+
+ Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());
+ encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, context.SecureRandom));
+
+ try
+ {
+ byte[] encryptedPreMasterSecret = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length);
+
+ if (TlsUtilities.IsSsl(context))
+ {
+ // TODO Do any SSLv3 servers actually expect the length?
+ output.Write(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length);
+ }
+ else
+ {
+ TlsUtilities.WriteOpaque16(encryptedPreMasterSecret, output);
+ }
+ }
+ catch (InvalidCipherTextException e)
+ {
+ /*
+ * This should never happen, only during decryption.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+
+ return premasterSecret;
+ }
+
+ public static byte[] SafeDecryptPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPrivateKey,
+ byte[] encryptedPreMasterSecret)
+ {
+ /*
+ * RFC 5246 7.4.7.1.
+ */
+ ProtocolVersion clientVersion = context.ClientVersion;
+
+ // TODO Provide as configuration option?
+ bool versionNumberCheckDisabled = false;
+
+ /*
+ * Generate 48 random bytes we can use as a Pre-Master-Secret, if the
+ * PKCS1 padding check should fail.
+ */
+ byte[] fallback = new byte[48];
+ context.SecureRandom.NextBytes(fallback);
+
+ byte[] M = Arrays.Clone(fallback);
+ try
+ {
+ Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback);
+ encoding.Init(false,
+ new ParametersWithRandom(rsaServerPrivateKey, context.SecureRandom));
+
+ M = encoding.ProcessBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length);
+ }
+ catch (Exception)
+ {
+ /*
+ * This should never happen since the decryption should never throw an exception
+ * and return a random value instead.
+ *
+ * In any case, a TLS server MUST NOT generate an alert if processing an
+ * RSA-encrypted premaster secret message fails, or the version number is not as
+ * expected. Instead, it MUST continue the handshake with a randomly generated
+ * premaster secret.
+ */
+ }
+
+ /*
+ * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
+ * check the version number [..].
+ */
+ if (versionNumberCheckDisabled && clientVersion.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
+ {
+ /*
+ * If the version number is TLS 1.0 or earlier, server
+ * implementations SHOULD check the version number, but MAY have a
+ * configuration option to disable the check.
+ *
+ * So there is nothing to do here.
+ */
+ }
+ else
+ {
+ /*
+ * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the
+ * clientVersion received during the handshake. If they don't match, we replace the
+ * decrypted Pre-Master-Secret with a random one.
+ */
+ int correct = (clientVersion.MajorVersion ^ (M[0] & 0xff))
+ | (clientVersion.MinorVersion ^ (M[1] & 0xff));
+ correct |= correct >> 1;
+ correct |= correct >> 2;
+ correct |= correct >> 4;
+ int mask = ~((correct & 1) - 1);
+
+ /*
+ * mask will be all bits set to 0xff if the version number differed.
+ */
+ for (int i = 0; i < 48; i++)
+ {
+ M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask));
+ }
+ }
+ return M;
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/TlsServer.cs b/crypto/src/crypto/tls/TlsServer.cs
new file mode 100644
index 000000000..93e62b9ac
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServer.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsServer
+ : TlsPeer
+ {
+ void Init(TlsServerContext context);
+
+ /// <exception cref="IOException"></exception>
+ void NotifyClientVersion(ProtocolVersion clientVersion);
+
+ /// <exception cref="IOException"></exception>
+ void NotifyOfferedCipherSuites(int[] offeredCipherSuites);
+
+ /// <exception cref="IOException"></exception>
+ void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods);
+
+ /// <param name="clientExtensions">A <see cref="IDictionary"/> (Int32 -> byte[]). Will never be null.</param>
+ /// <exception cref="IOException"></exception>
+ void ProcessClientExtensions(IDictionary clientExtensions);
+
+ /// <exception cref="IOException"></exception>
+ ProtocolVersion GetServerVersion();
+
+ /// <exception cref="IOException"></exception>
+ int GetSelectedCipherSuite();
+
+ /// <exception cref="IOException"></exception>
+ byte GetSelectedCompressionMethod();
+
+ /// <summary>
+ /// Get the (optional) table of server extensions to be included in (extended) server hello.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="IDictionary"/> (Int32 -> byte[]). May be null.
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ IDictionary GetServerExtensions();
+
+ /// <returns>
+ /// A <see cref="IList"/> (<see cref="SupplementalDataEntry"/>). May be null.
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ IList GetServerSupplementalData();
+
+ /// <exception cref="IOException"></exception>
+ TlsCredentials GetCredentials();
+
+ /// <remarks>
+ /// This method will be called (only) if the server included an extension of type
+ /// "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546
+ /// 3.6. Certificate Status Request</i>. If a non-null <see cref="CertificateStatus"/> is returned, it
+ /// is sent to the client as a handshake message of type "certificate_status".
+ /// </remarks>
+ /// <returns>A <see cref="CertificateStatus"/> to be sent to the client (or null for none).</returns>
+ /// <exception cref="IOException"></exception>
+ CertificateStatus GetCertificateStatus();
+
+ /// <exception cref="IOException"></exception>
+ TlsKeyExchange GetKeyExchange();
+
+ /// <exception cref="IOException"></exception>
+ CertificateRequest GetCertificateRequest();
+
+ /// <param name="clientSupplementalData"><see cref="IList"/> (<see cref="SupplementalDataEntry"/>)</param>
+ /// <exception cref="IOException"></exception>
+ void ProcessClientSupplementalData(IList clientSupplementalData);
+
+ /// <summary>
+ /// Called by the protocol handler to report the client certificate, only if <c>GetCertificateRequest</c>
+ /// returned non-null.
+ /// </summary>
+ /// <remarks>Note: this method is responsible for certificate verification and validation.</remarks>
+ /// <param name="clientCertificate">the effective client certificate (may be an empty chain).</param>
+ /// <exception cref="IOException"></exception>
+ void NotifyClientCertificate(Certificate clientCertificate);
+
+ /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message.</summary>
+ /// <remarks>
+ /// This method will be called (only) if a NewSessionTicket extension was sent by the server. See
+ /// <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection.
+ /// </remarks>
+ /// <returns>The <see cref="NewSessionTicket">ticket</see>)</returns>
+ /// <exception cref="IOException"></exception>
+ NewSessionTicket GetNewSessionTicket();
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsServerContext.cs b/crypto/src/crypto/tls/TlsServerContext.cs
new file mode 100644
index 000000000..4021571aa
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServerContext.cs
@@ -0,0 +1,11 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsServerContext
+ : TlsContext
+ {
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsServerContextImpl.cs b/crypto/src/crypto/tls/TlsServerContextImpl.cs
new file mode 100644
index 000000000..d56566ffc
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServerContextImpl.cs
@@ -0,0 +1,20 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsServerContextImpl
+ : AbstractTlsContext, TlsServerContext
+ {
+ internal TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+ : base(secureRandom, securityParameters)
+ {
+ }
+
+ public override bool IsServer
+ {
+ get { return true; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs
new file mode 100644
index 000000000..165d6a147
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -0,0 +1,753 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsServerProtocol
+ : TlsProtocol
+ {
+ protected TlsServer mTlsServer = null;
+ internal TlsServerContextImpl mTlsServerContext = null;
+
+ protected TlsKeyExchange mKeyExchange = null;
+ protected TlsCredentials mServerCredentials = null;
+ protected CertificateRequest mCertificateRequest = null;
+
+ protected short mClientCertificateType = -1;
+ protected TlsHandshakeHash mPrepareFinishHash = null;
+
+ public TlsServerProtocol(Stream stream, SecureRandom secureRandom)
+ : base(stream, secureRandom)
+ {
+ }
+
+ public TlsServerProtocol(Stream input, Stream output, SecureRandom secureRandom)
+ : base(input, output, secureRandom)
+ {
+ }
+
+ /**
+ * Receives a TLS handshake in the role of server
+ *
+ * @param mTlsServer
+ * @throws IOException If handshake was not successful.
+ */
+ public virtual void Accept(TlsServer tlsServer)
+ {
+ if (tlsServer == null)
+ throw new ArgumentNullException("tlsServer");
+ if (this.mTlsServer != null)
+ throw new InvalidOperationException("'Accept' can only be called once");
+
+ this.mTlsServer = tlsServer;
+
+ this.mSecurityParameters = new SecurityParameters();
+ this.mSecurityParameters.entity = ConnectionEnd.server;
+
+ this.mTlsServerContext = new TlsServerContextImpl(mSecureRandom, mSecurityParameters);
+
+ this.mSecurityParameters.serverRandom = CreateRandomBlock(tlsServer.ShouldUseGmtUnixTime(),
+ mTlsServerContext.NonceRandomGenerator);
+
+ this.mTlsServer.Init(mTlsServerContext);
+ this.mRecordStream.Init(mTlsServerContext);
+
+ this.mRecordStream.SetRestrictReadVersion(false);
+
+ CompleteHandshake();
+ }
+
+ protected override void CleanupHandshake()
+ {
+ base.CleanupHandshake();
+
+ this.mKeyExchange = null;
+ this.mServerCredentials = null;
+ this.mCertificateRequest = null;
+ this.mPrepareFinishHash = null;
+ }
+
+ protected override TlsContext Context
+ {
+ get { return mTlsServerContext; }
+ }
+
+ internal override AbstractTlsContext ContextAdmin
+ {
+ get { return mTlsServerContext; }
+ }
+
+ protected override TlsPeer Peer
+ {
+ get { return mTlsServer; }
+ }
+
+ protected override void HandleHandshakeMessage(byte type, byte[] data)
+ {
+ MemoryStream buf = new MemoryStream(data);
+
+ switch (type)
+ {
+ case HandshakeType.client_hello:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_START:
+ {
+ ReceiveClientHelloMessage(buf);
+ this.mConnectionState = CS_CLIENT_HELLO;
+
+ SendServerHelloMessage();
+ this.mConnectionState = CS_SERVER_HELLO;
+
+ IList serverSupplementalData = mTlsServer.GetServerSupplementalData();
+ if (serverSupplementalData != null)
+ {
+ SendSupplementalDataMessage(serverSupplementalData);
+ }
+ this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA;
+
+ this.mKeyExchange = mTlsServer.GetKeyExchange();
+ this.mKeyExchange.Init(Context);
+
+ this.mServerCredentials = mTlsServer.GetCredentials();
+
+ Certificate serverCertificate = null;
+
+ if (this.mServerCredentials == null)
+ {
+ this.mKeyExchange.SkipServerCredentials();
+ }
+ else
+ {
+ this.mKeyExchange.ProcessServerCredentials(this.mServerCredentials);
+
+ serverCertificate = this.mServerCredentials.Certificate;
+ SendCertificateMessage(serverCertificate);
+ }
+ this.mConnectionState = CS_SERVER_CERTIFICATE;
+
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (serverCertificate == null || serverCertificate.IsEmpty)
+ {
+ this.mAllowCertificateStatus = false;
+ }
+
+ if (this.mAllowCertificateStatus)
+ {
+ CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus();
+ if (certificateStatus != null)
+ {
+ SendCertificateStatusMessage(certificateStatus);
+ }
+ }
+
+ this.mConnectionState = CS_CERTIFICATE_STATUS;
+
+ byte[] serverKeyExchange = this.mKeyExchange.GenerateServerKeyExchange();
+ if (serverKeyExchange != null)
+ {
+ SendServerKeyExchangeMessage(serverKeyExchange);
+ }
+ this.mConnectionState = CS_SERVER_KEY_EXCHANGE;
+
+ if (this.mServerCredentials != null)
+ {
+ this.mCertificateRequest = mTlsServer.GetCertificateRequest();
+ if (this.mCertificateRequest != null)
+ {
+ this.mKeyExchange.ValidateCertificateRequest(mCertificateRequest);
+
+ SendCertificateRequestMessage(mCertificateRequest);
+
+ TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
+ this.mCertificateRequest.SupportedSignatureAlgorithms);
+ }
+ }
+ this.mConnectionState = CS_CERTIFICATE_REQUEST;
+
+ SendServerHelloDoneMessage();
+ this.mConnectionState = CS_SERVER_HELLO_DONE;
+
+ this.mRecordStream.HandshakeHash.SealHashAlgorithms();
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.supplemental_data:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ {
+ mTlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf));
+ this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ {
+ if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
+ {
+ mTlsServer.ProcessClientSupplementalData(null);
+ }
+
+ if (this.mCertificateRequest == null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ReceiveCertificateMessage(buf);
+ this.mConnectionState = CS_CLIENT_CERTIFICATE;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.client_key_exchange:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO_DONE:
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ case CS_CLIENT_CERTIFICATE:
+ {
+ if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
+ {
+ mTlsServer.ProcessClientSupplementalData(null);
+ }
+
+ if (mConnectionState < CS_CLIENT_CERTIFICATE)
+ {
+ if (this.mCertificateRequest == null)
+ {
+ this.mKeyExchange.SkipClientCredentials();
+ }
+ else
+ {
+ if (TlsUtilities.IsTlsV12(Context))
+ {
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST Send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ else if (TlsUtilities.IsSsl(Context))
+ {
+ if (this.mPeerCertificate == null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ else
+ {
+ NotifyClientCertificate(Certificate.EmptyChain);
+ }
+ }
+ }
+
+ ReceiveClientKeyExchangeMessage(buf);
+ this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.certificate_verify:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_KEY_EXCHANGE:
+ {
+ /*
+ * RFC 5246 7.4.8 This message is only sent following a client certificate that has
+ * signing capability (i.e., all certificates except those containing fixed
+ * Diffie-Hellman parameters).
+ */
+ if (!ExpectCertificateVerifyMessage())
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ReceiveCertificateVerifyMessage(buf);
+ this.mConnectionState = CS_CERTIFICATE_VERIFY;
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_KEY_EXCHANGE:
+ case CS_CERTIFICATE_VERIFY:
+ {
+ if (mConnectionState < CS_CERTIFICATE_VERIFY && ExpectCertificateVerifyMessage())
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ProcessFinishedMessage(buf);
+ this.mConnectionState = CS_CLIENT_FINISHED;
+
+ if (this.mExpectSessionTicket)
+ {
+ SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket());
+ SendChangeCipherSpecMessage();
+ }
+ this.mConnectionState = CS_SERVER_SESSION_TICKET;
+
+ SendFinishedMessage();
+ this.mConnectionState = CS_SERVER_FINISHED;
+ this.mConnectionState = CS_END;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.hello_request:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.server_hello:
+ case HandshakeType.server_key_exchange:
+ case HandshakeType.certificate_request:
+ case HandshakeType.server_hello_done:
+ case HandshakeType.session_ticket:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ protected override void HandleWarningMessage(byte description)
+ {
+ switch (description)
+ {
+ case AlertDescription.no_certificate:
+ {
+ /*
+ * SSL 3.0 If the server has sent a certificate request Message, the client must Send
+ * either the certificate message or a no_certificate alert.
+ */
+ if (TlsUtilities.IsSsl(Context) && mCertificateRequest != null)
+ {
+ NotifyClientCertificate(Certificate.EmptyChain);
+ }
+ break;
+ }
+ default:
+ {
+ base.HandleWarningMessage(description);
+ break;
+ }
+ }
+ }
+
+ protected virtual void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ if (mCertificateRequest == null)
+ throw new InvalidOperationException();
+ if (mPeerCertificate != null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ this.mPeerCertificate = clientCertificate;
+
+ if (clientCertificate.IsEmpty)
+ {
+ this.mKeyExchange.SkipClientCredentials();
+ }
+ else
+ {
+
+ /*
+ * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request
+ * message was non-empty, one of the certificates in the certificate chain SHOULD be
+ * issued by one of the listed CAs.
+ */
+
+ this.mClientCertificateType = TlsUtilities.GetClientCertificateType(clientCertificate,
+ this.mServerCredentials.Certificate);
+
+ this.mKeyExchange.ProcessClientCertificate(clientCertificate);
+ }
+
+ /*
+ * RFC 5246 7.4.6. If the client does not Send any certificates, the server MAY at its
+ * discretion either continue the handshake without client authentication, or respond with a
+ * fatal handshake_failure alert. Also, if some aspect of the certificate chain was
+ * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its
+ * discretion either continue the handshake (considering the client unauthenticated) or Send
+ * a fatal alert.
+ */
+ this.mTlsServer.NotifyClientCertificate(clientCertificate);
+ }
+
+ protected virtual void ReceiveCertificateMessage(MemoryStream buf)
+ {
+ Certificate clientCertificate = Certificate.Parse(buf);
+
+ AssertEmpty(buf);
+
+ NotifyClientCertificate(clientCertificate);
+ }
+
+ protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf)
+ {
+ DigitallySigned clientCertificateVerify = DigitallySigned.Parse(Context, buf);
+
+ AssertEmpty(buf);
+
+ // Verify the CertificateVerify message contains a correct signature.
+ try
+ {
+ byte[] hash;
+ if (TlsUtilities.IsTlsV12(Context))
+ {
+ hash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash);
+ }
+ else
+ {
+ hash = mSecurityParameters.SessionHash;
+ }
+
+ X509CertificateStructure x509Cert = mPeerCertificate.GetCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo);
+
+ TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType);
+ tlsSigner.Init(Context);
+ if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm,
+ clientCertificateVerify.Signature, publicKey, hash))
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error, e);
+ }
+ }
+
+ protected virtual void ReceiveClientHelloMessage(MemoryStream buf)
+ {
+ ProtocolVersion client_version = TlsUtilities.ReadVersion(buf);
+ if (client_version.IsDtls)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ byte[] client_random = TlsUtilities.ReadFully(32, buf);
+
+ /*
+ * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to
+ * use the Session ID in the ClientHello for stateful session resumption.
+ */
+ byte[] sessionID = TlsUtilities.ReadOpaque8(buf);
+ if (sessionID.Length > 32)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ /*
+ * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session
+ * resumption request), this vector MUST include at least the cipher_suite from that
+ * session.
+ */
+ int cipher_suites_length = TlsUtilities.ReadUint16(buf);
+ if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ this.mOfferedCipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, buf);
+
+ /*
+ * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session
+ * resumption request), it MUST include the compression_method from that session.
+ */
+ int compression_methods_length = TlsUtilities.ReadUint8(buf);
+ if (compression_methods_length < 1)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mOfferedCompressionMethods = TlsUtilities.ReadUint8Array(compression_methods_length, buf);
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and Send a server hello containing no
+ * extensions.
+ */
+ this.mClientExtensions = ReadExtensions(buf);
+
+ this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions);
+
+ ContextAdmin.SetClientVersion(client_version);
+
+ mTlsServer.NotifyClientVersion(client_version);
+
+ mSecurityParameters.clientRandom = client_random;
+
+ mTlsServer.NotifyOfferedCipherSuites(mOfferedCipherSuites);
+ mTlsServer.NotifyOfferedCompressionMethods(mOfferedCompressionMethods);
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+
+ /*
+ * When a ClientHello is received, the server MUST check if it includes the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
+ * to TRUE.
+ */
+ if (Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ {
+ this.mSecureRenegotiation = true;
+ }
+
+ /*
+ * The server MUST check if the "renegotiation_info" extension is included in the
+ * ClientHello.
+ */
+ byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info);
+ if (renegExtData != null)
+ {
+ /*
+ * If the extension is present, set secure_renegotiation flag to TRUE. The
+ * server MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake.
+ */
+ this.mSecureRenegotiation = true;
+
+ if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ mTlsServer.NotifySecureRenegotiation(this.mSecureRenegotiation);
+
+ if (mClientExtensions != null)
+ {
+ mTlsServer.ProcessClientExtensions(mClientExtensions);
+ }
+ }
+
+ protected virtual void ReceiveClientKeyExchangeMessage(MemoryStream buf)
+ {
+ mKeyExchange.ProcessClientKeyExchange(buf);
+
+ AssertEmpty(buf);
+
+ this.mPrepareFinishHash = mRecordStream.PrepareToFinish();
+ this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, mPrepareFinishHash, null);
+
+ EstablishMasterSecret(Context, mKeyExchange);
+ mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
+
+ if (!mExpectSessionTicket)
+ {
+ SendChangeCipherSpecMessage();
+ }
+ }
+
+ protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request);
+
+ certificateRequest.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendCertificateStatusMessage(CertificateStatus certificateStatus)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status);
+
+ certificateStatus.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendNewSessionTicketMessage(NewSessionTicket newSessionTicket)
+ {
+ if (newSessionTicket == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket);
+
+ newSessionTicket.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendServerHelloMessage()
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello);
+
+ ProtocolVersion server_version = mTlsServer.GetServerVersion();
+ if (!server_version.IsEqualOrEarlierVersionOf(Context.ClientVersion))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ mRecordStream.ReadVersion = server_version;
+ mRecordStream.SetWriteVersion(server_version);
+ mRecordStream.SetRestrictReadVersion(true);
+ ContextAdmin.SetServerVersion(server_version);
+
+ TlsUtilities.WriteVersion(server_version, message);
+
+ message.Write(this.mSecurityParameters.serverRandom);
+
+ /*
+ * The server may return an empty session_id to indicate that the session will not be cached
+ * and therefore cannot be resumed.
+ */
+ TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, message);
+
+ int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite();
+ if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite)
+ || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ mSecurityParameters.cipherSuite = selectedCipherSuite;
+
+ byte selectedCompressionMethod = mTlsServer.GetSelectedCompressionMethod();
+ if (!Arrays.Contains(mOfferedCompressionMethods, selectedCompressionMethod))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;
+
+ TlsUtilities.WriteUint16(selectedCipherSuite, message);
+ TlsUtilities.WriteUint8(selectedCompressionMethod, message);
+
+ this.mServerExtensions = mTlsServer.GetServerExtensions();
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ if (this.mSecureRenegotiation)
+ {
+ byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info);
+ bool noRenegExt = (null == renegExtData);
+
+ if (noRenegExt)
+ {
+ /*
+ * Note that Sending a "renegotiation_info" extension in response to a ClientHello
+ * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
+ * Section 7.4.1.4, on the server Sending unsolicited extensions and is only allowed
+ * because the client is signaling its willingness to receive the extension via the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+
+ /*
+ * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
+ * "renegotiation_info" extension in the ServerHello message.
+ */
+ this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions);
+ this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
+ }
+ }
+
+ if (mSecurityParameters.extendedMasterSecret)
+ {
+ this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions);
+ TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions);
+ }
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and Send a server hello containing no
+ * extensions.
+ */
+
+ if (this.mServerExtensions != null)
+ {
+ this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions);
+
+ this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(mClientExtensions,
+ mServerExtensions, AlertDescription.internal_error);
+
+ this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions);
+
+ /*
+ * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+ * a session resumption handshake.
+ */
+ this.mAllowCertificateStatus = !mResumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.status_request,
+ AlertDescription.internal_error);
+
+ this.mExpectSessionTicket = !mResumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.session_ticket,
+ AlertDescription.internal_error);
+
+ WriteExtensions(message, this.mServerExtensions);
+ }
+
+ if (mSecurityParameters.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + mSecurityParameters.maxFragmentLength);
+ mRecordStream.SetPlaintextLimit(plainTextLimit);
+ }
+
+ mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite);
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
+ * a verify_data_length equal to 12. This includes all existing cipher suites.
+ */
+ mSecurityParameters.verifyDataLength = 12;
+
+ message.WriteToRecordStream(this);
+
+ this.mRecordStream.NotifyHelloComplete();
+ }
+
+ protected virtual void SendServerHelloDoneMessage()
+ {
+ byte[] message = new byte[4];
+ TlsUtilities.WriteUint8(HandshakeType.server_hello_done, message, 0);
+ TlsUtilities.WriteUint24(0, message, 1);
+
+ WriteHandshakeMessage(message, 0, message.Length);
+ }
+
+ protected virtual void SendServerKeyExchangeMessage(byte[] serverKeyExchange)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.Length);
+
+ message.Write(serverKeyExchange);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual bool ExpectCertificateVerifyMessage()
+ {
+ return mClientCertificateType >= 0 && TlsUtilities.HasSigningCapability((byte)mClientCertificateType);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs
new file mode 100644
index 000000000..866392623
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSessionImpl.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsSessionImpl
+ : TlsSession
+ {
+ internal readonly byte[] mSessionID;
+ internal SessionParameters mSessionParameters;
+
+ internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ if (sessionID == null)
+ throw new ArgumentNullException("sessionID");
+ if (sessionID.Length < 1 || sessionID.Length > 32)
+ throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID");
+
+ this.mSessionID = Arrays.Clone(sessionID);
+ this.mSessionParameters = sessionParameters;
+ }
+
+ public virtual SessionParameters ExportSessionParameters()
+ {
+ lock (this)
+ {
+ return this.mSessionParameters == null ? null : this.mSessionParameters.Copy();
+ }
+ }
+
+ public virtual byte[] SessionID
+ {
+ get { lock (this) return mSessionID; }
+ }
+
+ public virtual void Invalidate()
+ {
+ lock (this)
+ {
+ if (this.mSessionParameters != null)
+ {
+ this.mSessionParameters.Clear();
+ this.mSessionParameters = null;
+ }
+ }
+ }
+
+ public virtual bool IsResumable
+ {
+ get { lock (this) return this.mSessionParameters != null; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsSigner.cs b/crypto/src/crypto/tls/TlsSigner.cs
index 79d468fee..ffdd4c9a1 100644
--- a/crypto/src/crypto/tls/TlsSigner.cs
+++ b/crypto/src/crypto/tls/TlsSigner.cs
@@ -1,18 +1,29 @@
using System;
-using Org.BouncyCastle.Security;
-
namespace Org.BouncyCastle.Crypto.Tls
{
public interface TlsSigner
{
- byte[] GenerateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey,
- byte[] md5andsha1);
- bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1);
+ void Init(TlsContext context);
+
+ byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1);
+
+ byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+ AsymmetricKeyParameter privateKey, byte[] hash);
+
+ bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1);
+
+ bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+ AsymmetricKeyParameter publicKey, byte[] hash);
+
+ ISigner CreateSigner(AsymmetricKeyParameter privateKey);
+
+ ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey);
- ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey);
ISigner CreateVerifyer(AsymmetricKeyParameter publicKey);
+ ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey);
+
bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
}
}
diff --git a/crypto/src/crypto/tls/TlsSignerCredentials.cs b/crypto/src/crypto/tls/TlsSignerCredentials.cs
index 2adb06c26..92ed7cc19 100644
--- a/crypto/src/crypto/tls/TlsSignerCredentials.cs
+++ b/crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -3,9 +3,12 @@ using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- public interface TlsSignerCredentials : TlsCredentials
- {
- /// <exception cref="IOException"></exception>
- byte[] GenerateCertificateSignature(byte[] md5andsha1);
- }
+ public interface TlsSignerCredentials
+ : TlsCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ byte[] GenerateCertificateSignature(byte[] hash);
+
+ SignatureAndHashAlgorithm SignatureAndHashAlgorithm { get; }
+ }
}
diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
index 950be87ba..f42f7456d 100644
--- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -1,200 +1,186 @@
using System;
+using System.Collections;
using System.IO;
-using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Agreement.Srp;
-using Org.BouncyCastle.Crypto.Digests;
-using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <summary>
- /// TLS 1.1 SRP key exchange.
- /// </summary>
- internal class TlsSrpKeyExchange
- : TlsKeyExchange
+ /// <summary>(D)TLS SRP key exchange (RFC 5054).</summary>
+ public class TlsSrpKeyExchange
+ : AbstractTlsKeyExchange
{
- protected TlsClientContext context;
- protected int keyExchange;
- protected TlsSigner tlsSigner;
- protected byte[] identity;
- protected byte[] password;
+ protected TlsSigner mTlsSigner;
+ protected byte[] mIdentity;
+ protected byte[] mPassword;
- protected AsymmetricKeyParameter serverPublicKey = null;
+ protected AsymmetricKeyParameter mServerPublicKey = null;
- protected byte[] s = null;
- protected BigInteger B = null;
- protected Srp6Client srpClient = new Srp6Client();
+ protected byte[] mS = null;
+ protected BigInteger mB = null;
+ protected Srp6Client mSrpClient = new Srp6Client();
- internal TlsSrpKeyExchange(TlsClientContext context, int keyExchange,
- byte[] identity, byte[] password)
+ public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity, byte[] password)
+ : base(keyExchange, supportedSignatureAlgorithms)
{
switch (keyExchange)
{
- case KeyExchangeAlgorithm.SRP:
- this.tlsSigner = null;
- break;
- case KeyExchangeAlgorithm.SRP_RSA:
- this.tlsSigner = new TlsRsaSigner();
- break;
- case KeyExchangeAlgorithm.SRP_DSS:
- this.tlsSigner = new TlsDssSigner();
- break;
- default:
- throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ case KeyExchangeAlgorithm.SRP:
+ this.mTlsSigner = null;
+ break;
+ case KeyExchangeAlgorithm.SRP_RSA:
+ this.mTlsSigner = new TlsRsaSigner();
+ break;
+ case KeyExchangeAlgorithm.SRP_DSS:
+ this.mTlsSigner = new TlsDssSigner();
+ break;
+ default:
+ throw new InvalidOperationException("unsupported key exchange algorithm");
}
- this.context = context;
- this.keyExchange = keyExchange;
- this.identity = identity;
- this.password = password;
+ this.mIdentity = identity;
+ this.mPassword = password;
}
- public virtual void SkipServerCertificate()
+ public override void Init(TlsContext context)
{
- if (tlsSigner != null)
+ base.Init(context);
+
+ if (this.mTlsSigner != null)
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ this.mTlsSigner.Init(context);
}
}
- public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ public override void SkipServerCredentials()
{
- if (tlsSigner == null)
- {
+ if (mTlsSigner != null)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
+ }
+
+ public override void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ if (mTlsSigner == null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ if (serverCertificate.IsEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
- SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
try
{
- this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
}
-// catch (RuntimeException)
- catch (Exception)
+ catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
- if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
- {
+ if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey))
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
- }
TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
- // TODO
- /*
- * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
- * signing algorithm for the certificate must be the same as the algorithm for the
- * certificate key."
- */
+ base.ProcessServerCertificate(serverCertificate);
}
- public virtual void SkipServerKeyExchange()
+ public override bool RequiresServerKeyExchange
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ get { return true; }
}
- public virtual void ProcessServerKeyExchange(Stream input)
+ public override void ProcessServerKeyExchange(Stream input)
{
SecurityParameters securityParameters = context.SecurityParameters;
- Stream sigIn = input;
- ISigner signer = null;
+ SignerInputBuffer buf = null;
+ Stream teeIn = input;
- if (tlsSigner != null)
+ if (mTlsSigner != null)
{
- signer = InitSigner(tlsSigner, securityParameters);
- sigIn = new SignerStream(input, signer, null);
+ buf = new SignerInputBuffer();
+ teeIn = new TeeInputStream(input, buf);
}
- byte[] NBytes = TlsUtilities.ReadOpaque16(sigIn);
- byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
- byte[] sBytes = TlsUtilities.ReadOpaque8(sigIn);
- byte[] BBytes = TlsUtilities.ReadOpaque16(sigIn);
+ byte[] NBytes = TlsUtilities.ReadOpaque16(teeIn);
+ byte[] gBytes = TlsUtilities.ReadOpaque16(teeIn);
+ byte[] sBytes = TlsUtilities.ReadOpaque8(teeIn);
+ byte[] BBytes = TlsUtilities.ReadOpaque16(teeIn);
- if (signer != null)
+ if (buf != null)
{
- byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+ DigitallySigned signed_params = DigitallySigned.Parse(context, input);
- if (!signer.VerifySignature(sigByte))
- {
+ ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
+ buf.UpdateSigner(signer);
+ if (!signer.VerifySignature(signed_params.Signature))
throw new TlsFatalAlert(AlertDescription.decrypt_error);
- }
}
BigInteger N = new BigInteger(1, NBytes);
BigInteger g = new BigInteger(1, gBytes);
// TODO Validate group parameters (see RFC 5054)
- //throw new TlsFatalAlert(AlertDescription.insufficient_security);
+ // throw new TlsFatalAlert(AlertDescription.insufficient_security);
- this.s = sBytes;
+ this.mS = sBytes;
/*
- * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter"
- * alert if B % N = 0.
- */
+ * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
+ * B % N = 0.
+ */
try
{
- this.B = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
+ this.mB = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
}
- catch (CryptoException)
+ catch (CryptoException e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
- this.srpClient.Init(N, g, new Sha1Digest(), context.SecureRandom);
+ this.mSrpClient.Init(N, g, TlsUtilities.CreateHash(HashAlgorithm.sha1), context.SecureRandom);
}
- public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public virtual void SkipClientCredentials()
- {
- // OK
- }
-
- public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- public virtual void GenerateClientKeyExchange(Stream output)
+ public override void GenerateClientKeyExchange(Stream output)
{
- byte[] keData = BigIntegers.AsUnsignedByteArray(srpClient.GenerateClientCredentials(s,
- this.identity, this.password));
- TlsUtilities.WriteOpaque16(keData, output);
+ BigInteger A = mSrpClient.GenerateClientCredentials(mS, this.mIdentity, this.mPassword);
+ TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(A), output);
}
- public virtual byte[] GeneratePremasterSecret()
+ public override byte[] GeneratePremasterSecret()
{
try
{
// TODO Check if this needs to be a fixed size
- return BigIntegers.AsUnsignedByteArray(srpClient.CalculateSecret(B));
+ return BigIntegers.AsUnsignedByteArray(mSrpClient.CalculateSecret(mB));
}
- catch (CryptoException)
+ catch (CryptoException e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
- protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
+ SecurityParameters securityParameters)
{
- ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+ ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey);
signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
return signer;
diff --git a/crypto/src/crypto/tls/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs
new file mode 100644
index 000000000..bbb6ac280
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class TlsSrpUtilities
+ {
+ public static void AddSrpExtension(IDictionary extensions, byte[] identity)
+ {
+ extensions[ExtensionType.srp] = CreateSrpExtension(identity);
+ }
+
+ public static byte[] GetSrpExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.srp);
+ return extensionData == null ? null : ReadSrpExtension(extensionData);
+ }
+
+ public static byte[] CreateSrpExtension(byte[] identity)
+ {
+ if (identity == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeOpaque8(identity);
+ }
+
+ public static byte[] ReadSrpExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+ byte[] identity = TlsUtilities.ReadOpaque8(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return identity;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsSrtpUtilities.cs b/crypto/src/crypto/tls/TlsSrtpUtilities.cs
new file mode 100644
index 000000000..626c0e3a4
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrtpUtilities.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * RFC 5764 DTLS Extension to Establish Keys for SRTP.
+ */
+ public abstract class TlsSRTPUtils
+ {
+ public static void AddUseSrtpExtension(IDictionary extensions, UseSrtpData useSRTPData)
+ {
+ extensions[ExtensionType.use_srtp] = CreateUseSrtpExtension(useSRTPData);
+ }
+
+ public static UseSrtpData GetUseSrtpExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.use_srtp);
+ return extensionData == null ? null : ReadUseSrtpExtension(extensionData);
+ }
+
+ public static byte[] CreateUseSrtpExtension(UseSrtpData useSrtpData)
+ {
+ if (useSrtpData == null)
+ throw new ArgumentNullException("useSrtpData");
+
+ MemoryStream buf = new MemoryStream();
+
+ // SRTPProtectionProfiles
+ TlsUtilities.WriteUint16ArrayWithUint16Length(useSrtpData.ProtectionProfiles, buf);
+
+ // srtp_mki
+ TlsUtilities.WriteOpaque8(useSrtpData.Mki, buf);
+
+ return buf.ToArray();
+ }
+
+ public static UseSrtpData ReadUseSrtpExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, true);
+
+ // SRTPProtectionProfiles
+ int length = TlsUtilities.ReadUint16(buf);
+ if (length < 2 || (length & 1) != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ int[] protectionProfiles = TlsUtilities.ReadUint16Array(length / 2, buf);
+
+ // srtp_mki
+ byte[] mki = TlsUtilities.ReadOpaque8(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return new UseSrtpData(protectionProfiles, mki);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsStream.cs b/crypto/src/crypto/tls/TlsStream.cs
index fe24ad3ed..7ff7184e3 100644
--- a/crypto/src/crypto/tls/TlsStream.cs
+++ b/crypto/src/crypto/tls/TlsStream.cs
@@ -3,84 +3,83 @@ using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- internal class TlsStream
- : Stream
- {
- private readonly TlsProtocolHandler handler;
+ internal class TlsStream
+ : Stream
+ {
+ private readonly TlsProtocol handler;
- internal TlsStream(
- TlsProtocolHandler handler)
- {
- this.handler = handler;
- }
+ internal TlsStream(TlsProtocol handler)
+ {
+ this.handler = handler;
+ }
- public override bool CanRead
- {
- get { return !handler.IsClosed; }
- }
+ public override bool CanRead
+ {
+ get { return !handler.IsClosed; }
+ }
- public override bool CanSeek
- {
- get { return false; }
- }
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
- public override bool CanWrite
- {
- get { return !handler.IsClosed; }
- }
+ public override bool CanWrite
+ {
+ get { return !handler.IsClosed; }
+ }
- public override void Close()
- {
- handler.Close();
- }
+ public override void Close()
+ {
+ handler.Close();
+ }
- public override void Flush()
- {
- handler.Flush();
- }
+ public override void Flush()
+ {
+ handler.Flush();
+ }
public override long Length
- {
- get { throw new NotSupportedException(); }
- }
+ {
+ get { throw new NotSupportedException(); }
+ }
- public override long Position
+ public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
- public override int Read(byte[] buf, int off, int len)
- {
- return this.handler.ReadApplicationData(buf, off, len);
- }
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return this.handler.ReadApplicationData(buf, off, len);
+ }
- public override int ReadByte()
- {
- byte[] buf = new byte[1];
- if (this.Read(buf, 0, 1) <= 0)
- return -1;
- return buf[0];
- }
+ public override int ReadByte()
+ {
+ byte[] buf = new byte[1];
+ if (this.Read(buf, 0, 1) <= 0)
+ return -1;
+ return buf[0];
+ }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
+ {
+ throw new NotSupportedException();
+ }
- public override void Write(byte[] buf, int off, int len)
- {
- this.handler.WriteData(buf, off, len);
- }
+ public override void Write(byte[] buf, int off, int len)
+ {
+ this.handler.WriteData(buf, off, len);
+ }
- public override void WriteByte(byte b)
- {
- this.handler.WriteData(new byte[] { b }, 0, 1);
- }
- }
+ public override void WriteByte(byte b)
+ {
+ this.handler.WriteData(new byte[] { b }, 0, 1);
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/TlsStreamCipher.cs b/crypto/src/crypto/tls/TlsStreamCipher.cs
index 35f794d96..45f2a9a70 100644
--- a/crypto/src/crypto/tls/TlsStreamCipher.cs
+++ b/crypto/src/crypto/tls/TlsStreamCipher.cs
@@ -1,108 +1,164 @@
using System;
+using System.IO;
-using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Tls;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- public class TlsStreamCipher : TlsCipher
+ public class TlsStreamCipher
+ : TlsCipher
{
- protected TlsClientContext context;
+ protected readonly TlsContext context;
- protected IStreamCipher encryptCipher;
- protected IStreamCipher decryptCipher;
+ protected readonly IStreamCipher encryptCipher;
+ protected readonly IStreamCipher decryptCipher;
- protected TlsMac writeMac;
- protected TlsMac readMac;
+ protected readonly TlsMac writeMac;
+ protected readonly TlsMac readMac;
- public TlsStreamCipher(TlsClientContext context, IStreamCipher encryptCipher,
- IStreamCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
+ protected readonly bool usesNonce;
+
+ /// <exception cref="IOException"></exception>
+ public TlsStreamCipher(TlsContext context, IStreamCipher clientWriteCipher,
+ IStreamCipher serverWriteCipher, IDigest clientWriteDigest, IDigest serverWriteDigest,
+ int cipherKeySize, bool usesNonce)
{
+ bool isServer = context.IsServer;
+
this.context = context;
- this.encryptCipher = encryptCipher;
- this.decryptCipher = decryptCipher;
+ this.usesNonce = usesNonce;
- int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize()
- + readDigest.GetDigestSize();
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
- SecurityParameters securityParameters = context.SecurityParameters;
+ int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize()
+ + serverWriteDigest.GetDigestSize();
- byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion",
- TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
- prfSize);
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
int offset = 0;
// Init MACs
- writeMac = CreateTlsMac(writeDigest, keyBlock, ref offset);
- readMac = CreateTlsMac(readDigest, keyBlock, ref offset);
+ TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.GetDigestSize());
+ offset += clientWriteDigest.GetDigestSize();
+ TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.GetDigestSize());
+ offset += serverWriteDigest.GetDigestSize();
// Build keys
- KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
- KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+ KeyParameter clientWriteKey = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter serverWriteKey = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
- if (offset != prfSize)
+ if (offset != key_block_size)
+ {
throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
- // Init Ciphers
- encryptCipher.Init(true, encryptKey);
- decryptCipher.Init(false, decryptKey);
- }
-
- public byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len)
- {
- byte[] mac = writeMac.CalculateMac(type, plaintext, offset, len);
- int size = len + mac.Length;
+ ICipherParameters encryptParams, decryptParams;
+ if (isServer)
+ {
+ this.writeMac = serverWriteMac;
+ this.readMac = clientWriteMac;
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ encryptParams = serverWriteKey;
+ decryptParams = clientWriteKey;
+ }
+ else
+ {
+ this.writeMac = clientWriteMac;
+ this.readMac = serverWriteMac;
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ encryptParams = clientWriteKey;
+ decryptParams = serverWriteKey;
+ }
- byte[] outbuf = new byte[size];
+ if (usesNonce)
+ {
+ byte[] dummyNonce = new byte[8];
+ encryptParams = new ParametersWithIV(encryptParams, dummyNonce);
+ decryptParams = new ParametersWithIV(decryptParams, dummyNonce);
+ }
- encryptCipher.ProcessBytes(plaintext, offset, len, outbuf, 0);
- encryptCipher.ProcessBytes(mac, 0, mac.Length, outbuf, len);
+ this.encryptCipher.Init(true, encryptParams);
+ this.decryptCipher.Init(false, decryptParams);
+ }
- return outbuf;
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - writeMac.Size;
}
- public byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len)
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
{
- byte[] deciphered = new byte[len];
- decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0);
+ /*
+ * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That
+ * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+ * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+ * of the 16-bit epoch with the 48-bit sequence number.
+ */
+ if (usesNonce)
+ {
+ UpdateIV(encryptCipher, true, seqNo);
+ }
- int plaintextSize = deciphered.Length - readMac.Size;
- byte[] plainText = CopyData(deciphered, 0, plaintextSize);
+ byte[] outBuf = new byte[len + writeMac.Size];
- byte[] receivedMac = CopyData(deciphered, plaintextSize, readMac.Size);
- byte[] computedMac = readMac.CalculateMac(type, plainText, 0, plainText.Length);
+ encryptCipher.ProcessBytes(plaintext, offset, len, outBuf, 0);
- if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac))
- {
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
- }
+ byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len);
+ encryptCipher.ProcessBytes(mac, 0, mac.Length, outBuf, len);
- return plainText;
+ return outBuf;
}
- protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off)
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
{
- int len = digest.GetDigestSize();
- TlsMac mac = new TlsMac(digest, buf, off, len);
- off += len;
- return mac;
+ /*
+ * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That
+ * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+ * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+ * of the 16-bit epoch with the 48-bit sequence number.
+ */
+ if (usesNonce)
+ {
+ UpdateIV(decryptCipher, false, seqNo);
+ }
+
+ int macSize = readMac.Size;
+ if (len < macSize)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int plaintextLength = len - macSize;
+
+ byte[] deciphered = new byte[len];
+ decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0);
+ CheckMac(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength);
+ return Arrays.CopyOfRange(deciphered, 0, plaintextLength);
}
- protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len)
+ /// <exception cref="IOException"></exception>
+ protected virtual void CheckMac(long seqNo, byte type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen)
{
- KeyParameter key = new KeyParameter(buf, off, len);
- off += len;
- return key;
+ byte[] receivedMac = Arrays.CopyOfRange(recBuf, recStart, recEnd);
+ byte[] computedMac = readMac.CalculateMac(seqNo, type, calcBuf, calcOff, calcLen);
+
+ if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac))
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
- protected virtual byte[] CopyData(byte[] text, int offset, int len)
+ protected virtual void UpdateIV(IStreamCipher cipher, bool forEncryption, long seqNo)
{
- byte[] result = new byte[len];
- Array.Copy(text, offset, result, 0, len);
- return result;
+ byte[] nonce = new byte[8];
+ TlsUtilities.WriteUint64(seqNo, nonce, 0);
+ cipher.Init(forEncryption, new ParametersWithIV(null, nonce));
}
}
}
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index ffb2fc3e6..d571e5900 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -4,10 +4,13 @@ using System.IO;
using System.Text;
using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
using Org.BouncyCastle.Utilities.IO;
@@ -15,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];
@@ -489,26 +492,6 @@ namespace Org.BouncyCastle.Crypto.Tls
return uints;
}
- [Obsolete]
- public static void CheckVersion(byte[] ReadVersion)
- {
- if ((ReadVersion[0] != 3) || (ReadVersion[1] != 1))
- {
- throw new TlsFatalAlert(AlertDescription.protocol_version);
- }
- }
-
- [Obsolete]
- public static void CheckVersion(Stream input)
- {
- int i1 = input.ReadByte();
- int i2 = input.ReadByte();
- if ((i1 != 3) || (i2 != 1))
- {
- throw new TlsFatalAlert(AlertDescription.protocol_version);
- }
- }
-
public static ProtocolVersion ReadVersion(byte[] buf, int offset)
{
return ProtocolVersion.Get(buf[offset], buf[offset + 1]);
@@ -574,20 +557,6 @@ namespace Org.BouncyCastle.Crypto.Tls
buf[offset + 3] = (byte)t;
}
- [Obsolete]
- public static void WriteVersion(Stream output)
- {
- output.WriteByte(3);
- output.WriteByte(1);
- }
-
- [Obsolete]
- public static void WriteVersion(byte[] buf, int offset)
- {
- buf[offset] = 3;
- buf[offset + 1] = 1;
- }
-
public static void WriteVersion(ProtocolVersion version, Stream output)
{
output.WriteByte((byte)version.MajorVersion);
@@ -631,6 +600,80 @@ namespace Org.BouncyCastle.Crypto.Tls
return true;
}
+ public static TlsSession ImportSession(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ return new TlsSessionImpl(sessionID, sessionParameters);
+ }
+
+ public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion)
+ {
+ return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(clientVersion.GetEquivalentTLSVersion());
+ }
+
+ /**
+ * Add a 'signature_algorithms' extension to existing extensions.
+ *
+ * @param extensions A {@link Hashtable} to add the extension to.
+ * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @throws IOException
+ */
+ public static void AddSignatureAlgorithmsExtension(IDictionary extensions, IList supportedSignatureAlgorithms)
+ {
+ extensions[ExtensionType.signature_algorithms] = CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms);
+ }
+
+ /**
+ * Get a 'signature_algorithms' extension from extensions.
+ *
+ * @param extensions A {@link Hashtable} to get the extension from, if it is present.
+ * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null.
+ * @throws IOException
+ */
+ public static IList GetSignatureAlgorithmsExtension(IDictionary extensions)
+ {
+ byte[] extensionData = GetExtensionData(extensions, ExtensionType.signature_algorithms);
+ return extensionData == null ? null : ReadSignatureAlgorithmsExtension(extensionData);
+ }
+
+ /**
+ * Create a 'signature_algorithms' extension value.
+ *
+ * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @return A byte array suitable for use as an extension value.
+ * @throws IOException
+ */
+ public static byte[] CreateSignatureAlgorithmsExtension(IList supportedSignatureAlgorithms)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ // supported_signature_algorithms
+ EncodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf);
+
+ return buf.ToArray();
+ }
+
+ /**
+ * Read 'signature_algorithms' extension data.
+ *
+ * @param extensionData The extension data.
+ * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @throws IOException
+ */
+ public static IList ReadSignatureAlgorithmsExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ // supported_signature_algorithms
+ IList supported_signature_algorithms = ParseSupportedSignatureAlgorithms(false, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return supported_signature_algorithms;
+ }
+
public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous,
Stream output)
{
@@ -642,8 +685,8 @@ namespace Org.BouncyCastle.Crypto.Tls
// supported_signature_algorithms
int length = 2 * supportedSignatureAlgorithms.Count;
- TlsUtilities.CheckUint16(length);
- TlsUtilities.WriteUint16(length, output);
+ CheckUint16(length);
+ WriteUint16(length, output);
foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
{
@@ -660,37 +703,76 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
- internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size)
+ public static IList ParseSupportedSignatureAlgorithms(bool allowAnonymous, Stream input)
+ {
+ // supported_signature_algorithms
+ int length = ReadUint16(input);
+ if (length < 2 || (length & 1) != 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ int count = length / 2;
+ IList supportedSignatureAlgorithms = Platform.CreateArrayList(count);
+ for (int i = 0; i < count; ++i)
+ {
+ SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.Parse(input);
+ if (!allowAnonymous && entry.Signature == SignatureAlgorithm.anonymous)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+ * in Section 7.4.3. It MUST NOT appear in this extension.
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ supportedSignatureAlgorithms.Add(entry);
+ }
+ return supportedSignatureAlgorithms;
+ }
+
+ 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;
+ if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy)
+ return PRF_legacy(secret, label, labelSeed, size);
+
+ IDigest prfDigest = CreatePrfHash(prfAlgorithm);
byte[] buf = new byte[size];
- byte[] prf = new byte[size];
- HMacHash(new MD5Digest(), s1, ls, prf);
- HMacHash(new Sha1Digest(), s2, ls, buf);
- for (int i = 0; i < size; i++)
- {
- buf[i] ^= prf[i];
- }
+ HMacHash(prfDigest, secret, labelSeed, buf);
return buf;
}
- internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size)
+ public static byte[] PRF_legacy(byte[] secret, string asciiLabel, byte[] seed, int size)
{
- byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+ byte[] label = Strings.ToByteArray(asciiLabel);
byte[] labelSeed = Concat(label, seed);
- byte[] buf = new byte[size];
- 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)
@@ -733,13 +815,111 @@ namespace Org.BouncyCastle.Crypto.Tls
DerBitString ku = KeyUsage.GetInstance(ext);
int bits = ku.GetBytes()[0];
if ((bits & keyUsageBits) != keyUsageBits)
- {
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
- }
}
}
}
+ internal static byte[] CalculateKeyBlock(TlsContext context, int size)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ byte[] master_secret = securityParameters.MasterSecret;
+ byte[] seed = Concat(securityParameters.ServerRandom, securityParameters.ClientRandom);
+
+ if (IsSsl(context))
+ return CalculateKeyBlock_Ssl(master_secret, seed, size);
+
+ return PRF(context, master_secret, ExporterLabel.key_expansion, seed, size);
+ }
+
+ internal static byte[] CalculateKeyBlock_Ssl(byte[] master_secret, byte[] random, int size)
+ {
+ IDigest md5 = CreateHash(HashAlgorithm.md5);
+ IDigest sha1 = CreateHash(HashAlgorithm.sha1);
+ int md5Size = md5.GetDigestSize();
+ byte[] shatmp = new byte[sha1.GetDigestSize()];
+ byte[] tmp = new byte[size + md5Size];
+
+ int i = 0, pos = 0;
+ while (pos < size)
+ {
+ byte[] ssl3Const = SSL3_CONST[i];
+
+ sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length);
+ sha1.BlockUpdate(master_secret, 0, master_secret.Length);
+ sha1.BlockUpdate(random, 0, random.Length);
+ sha1.DoFinal(shatmp, 0);
+
+ md5.BlockUpdate(master_secret, 0, master_secret.Length);
+ md5.BlockUpdate(shatmp, 0, shatmp.Length);
+ md5.DoFinal(tmp, pos);
+
+ pos += md5Size;
+ ++i;
+ }
+
+ return Arrays.CopyOfRange(tmp, 0, size);
+ }
+
+ internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_master_secret)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ byte[] seed = securityParameters.extendedMasterSecret
+ ? securityParameters.SessionHash
+ : Concat(securityParameters.ClientRandom, securityParameters.ServerRandom);
+
+ if (IsSsl(context))
+ return CalculateMasterSecret_Ssl(pre_master_secret, seed);
+
+ string asciiLabel = securityParameters.extendedMasterSecret
+ ? ExporterLabel.extended_master_secret
+ : ExporterLabel.master_secret;
+
+ return PRF(context, pre_master_secret, asciiLabel, seed, 48);
+ }
+
+ internal static byte[] CalculateMasterSecret_Ssl(byte[] pre_master_secret, byte[] random)
+ {
+ IDigest md5 = CreateHash(HashAlgorithm.md5);
+ IDigest sha1 = CreateHash(HashAlgorithm.sha1);
+ int md5Size = md5.GetDigestSize();
+ byte[] shatmp = new byte[sha1.GetDigestSize()];
+
+ byte[] rval = new byte[md5Size * 3];
+ int pos = 0;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ byte[] ssl3Const = SSL3_CONST[i];
+
+ sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length);
+ sha1.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length);
+ sha1.BlockUpdate(random, 0, random.Length);
+ sha1.DoFinal(shatmp, 0);
+
+ md5.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length);
+ md5.BlockUpdate(shatmp, 0, shatmp.Length);
+ md5.DoFinal(rval, pos);
+
+ pos += md5Size;
+ }
+
+ return rval;
+ }
+
+ internal static byte[] CalculateVerifyData(TlsContext context, string asciiLabel, byte[] handshakeHash)
+ {
+ if (IsSsl(context))
+ return handshakeHash;
+
+ SecurityParameters securityParameters = context.SecurityParameters;
+ byte[] master_secret = securityParameters.MasterSecret;
+ int verify_data_length = securityParameters.VerifyDataLength;
+
+ return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length);
+ }
+
public static IDigest CreateHash(byte hashAlgorithm)
{
switch (hashAlgorithm)
@@ -782,6 +962,189 @@ 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");
+ }
+ }
+
+ internal static short GetClientCertificateType(Certificate clientCertificate, Certificate serverCertificate)
+ {
+ if (clientCertificate.IsEmpty)
+ return -1;
+
+ X509CertificateStructure x509Cert = clientCertificate.GetCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ try
+ {
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo);
+ if (publicKey.IsPrivate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ /*
+ * TODO RFC 5246 7.4.6. The certificates MUST be signed using an acceptable hash/
+ * signature algorithm pair, as described in Section 7.4.4. Note that this relaxes the
+ * constraints on certificate-signing algorithms found in prior versions of TLS.
+ */
+
+ /*
+ * RFC 5246 7.4.6. Client Certificate
+ */
+
+ /*
+ * RSA public key; the certificate MUST allow the key to be used for signing with the
+ * signature scheme and hash algorithm that will be employed in the certificate verify
+ * message.
+ */
+ if (publicKey is RsaKeyParameters)
+ {
+ ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ return ClientCertificateType.rsa_sign;
+ }
+
+ /*
+ * DSA public key; the certificate MUST allow the key to be used for signing with the
+ * hash algorithm that will be employed in the certificate verify message.
+ */
+ if (publicKey is DsaPublicKeyParameters)
+ {
+ ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ return ClientCertificateType.dss_sign;
+ }
+
+ /*
+ * ECDSA-capable public key; the certificate MUST allow the key to be used for signing
+ * with the hash algorithm that will be employed in the certificate verify message; the
+ * public key MUST use a curve and point format supported by the server.
+ */
+ if (publicKey is ECPublicKeyParameters)
+ {
+ ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ // TODO Check the curve and point format
+ return ClientCertificateType.ecdsa_sign;
+ }
+
+ // TODO Add support for ClientCertificateType.*_fixed_*
+
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
+ }
+ }
+
+ internal static void TrackHashAlgorithms(TlsHandshakeHash handshakeHash, IList supportedSignatureAlgorithms)
+ {
+ if (supportedSignatureAlgorithms != null)
+ {
+ foreach (SignatureAndHashAlgorithm signatureAndHashAlgorithm in supportedSignatureAlgorithms)
+ {
+ byte hashAlgorithm = signatureAndHashAlgorithm.Hash;
+ handshakeHash.TrackHashAlgorithm(hashAlgorithm);
+ }
+ }
+ }
+
+ public static bool HasSigningCapability(byte clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_sign:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static TlsSigner CreateTlsSigner(byte clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ return new TlsDssSigner();
+ case ClientCertificateType.ecdsa_sign:
+ return new TlsECDsaSigner();
+ case ClientCertificateType.rsa_sign:
+ return new TlsRsaSigner();
+ default:
+ throw new ArgumentException("not a type with signing capability", "clientCertificateType");
+ }
+ }
+
+ internal static readonly byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54};
+ internal static readonly byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52};
+
+ // SSL3 magic mix constants ("A", "BB", "CCC", ...)
+ internal static readonly byte[][] SSL3_CONST = GenSsl3Const();
+
+ private static byte[][] GenSsl3Const()
+ {
+ int n = 10;
+ byte[][] arr = new byte[n][];
+ for (int i = 0; i < n; i++)
+ {
+ byte[] b = new byte[i + 1];
+ Arrays.Fill(b, (byte)('A' + i));
+ arr[i] = b;
+ }
+ return arr;
+ }
+
private static IList VectorOfOne(object obj)
{
IList v = Platform.CreateArrayList(1);
diff --git a/crypto/src/crypto/tls/UseSrtpData.cs b/crypto/src/crypto/tls/UseSrtpData.cs
new file mode 100644
index 000000000..fe8f8accb
--- /dev/null
+++ b/crypto/src/crypto/tls/UseSrtpData.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * RFC 5764 4.1.1
+ */
+ public class UseSrtpData
+ {
+ protected readonly int[] mProtectionProfiles;
+ protected readonly byte[] mMki;
+
+ /**
+ * @param protectionProfiles see {@link SrtpProtectionProfile} for valid constants.
+ * @param mki valid lengths from 0 to 255.
+ */
+ public UseSrtpData(int[] protectionProfiles, byte[] mki)
+ {
+ if (protectionProfiles == null || protectionProfiles.Length < 1
+ || protectionProfiles.Length >= (1 << 15))
+ {
+ throw new ArgumentException("must have length from 1 to (2^15 - 1)", "protectionProfiles");
+ }
+
+ if (mki == null)
+ {
+ mki = TlsUtilities.EmptyBytes;
+ }
+ else if (mki.Length > 255)
+ {
+ throw new ArgumentException("cannot be longer than 255 bytes", "mki");
+ }
+
+ this.mProtectionProfiles = protectionProfiles;
+ this.mMki = mki;
+ }
+
+ /**
+ * @return see {@link SrtpProtectionProfile} for valid constants.
+ */
+ public virtual int[] ProtectionProfiles
+ {
+ get { return mProtectionProfiles; }
+ }
+
+ /**
+ * @return valid lengths from 0 to 255.
+ */
+ public virtual byte[] Mki
+ {
+ get { return mMki; }
+ }
+ }
+}
diff --git a/crypto/src/openssl/PEMReader.cs b/crypto/src/openssl/PEMReader.cs
index 9d3560838..8c19fe601 100644
--- a/crypto/src/openssl/PEMReader.cs
+++ b/crypto/src/openssl/PEMReader.cs
@@ -276,7 +276,7 @@ namespace Org.BouncyCastle.OpenSsl
if (seq.Count != 9)
throw new PemException("malformed sequence in RSA private key");
- RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq);
+ RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(seq);
pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent);
privSpec = new RsaPrivateCrtKeyParameters(
diff --git a/crypto/src/pkcs/Pkcs12Store.cs b/crypto/src/pkcs/Pkcs12Store.cs
index 40364eec7..e4fe29401 100644
--- a/crypto/src/pkcs/Pkcs12Store.cs
+++ b/crypto/src/pkcs/Pkcs12Store.cs
@@ -28,6 +28,8 @@ namespace Org.BouncyCastle.Pkcs
private readonly DerObjectIdentifier certAlgorithm;
private readonly bool useDerEncoding;
+ private AsymmetricKeyEntry unmarkedKeyEntry = null;
+
private const int MinIterations = 1024;
private const int SaltSize = 20;
@@ -107,22 +109,101 @@ namespace Org.BouncyCastle.Pkcs
Load(input, password);
}
+ protected virtual void LoadKeyBag(PrivateKeyInfo privKeyInfo, Asn1Set bagAttributes)
+ {
+ AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo);
+
+ IDictionary attributes = Platform.CreateHashtable();
+ AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privKey, attributes);
+
+ string alias = null;
+ Asn1OctetString localId = null;
+
+ if (bagAttributes != null)
+ {
+ foreach (Asn1Sequence sq in bagAttributes)
+ {
+ DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]);
+ Asn1Set attrSet = Asn1Set.GetInstance(sq[1]);
+ Asn1Encodable attr = null;
+
+ if (attrSet.Count > 0)
+ {
+ // TODO We should be adding all attributes in the set
+ attr = attrSet[0];
+
+ // TODO We might want to "merge" attribute sets with
+ // the same OID - currently, differing values give an error
+ if (attributes.Contains(aOid.Id))
+ {
+ // OK, but the value has to be the same
+ if (!attributes[aOid.Id].Equals(attr))
+ throw new IOException("attempt to add existing attribute with different value");
+ }
+ else
+ {
+ attributes.Add(aOid.Id, attr);
+ }
+
+ if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
+ {
+ alias = ((DerBmpString)attr).GetString();
+ // TODO Do these in a separate loop, just collect aliases here
+ keys[alias] = keyEntry;
+ }
+ else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
+ {
+ localId = (Asn1OctetString)attr;
+ }
+ }
+ }
+ }
+
+ if (localId != null)
+ {
+ string name = Hex.ToHexString(localId.GetOctets());
+
+ if (alias == null)
+ {
+ keys[name] = keyEntry;
+ }
+ else
+ {
+ // TODO There may have been more than one alias
+ localIds[alias] = name;
+ }
+ }
+ else
+ {
+ unmarkedKeyEntry = keyEntry;
+ }
+ }
+
+ protected virtual void LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo encPrivKeyInfo, Asn1Set bagAttributes,
+ char[] password, bool wrongPkcs12Zero)
+ {
+ if (password != null)
+ {
+ PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
+ password, wrongPkcs12Zero, encPrivKeyInfo);
+
+ LoadKeyBag(privInfo, bagAttributes);
+ }
+ }
+
public void Load(
Stream input,
char[] password)
{
if (input == null)
throw new ArgumentNullException("input");
- if (password == null)
- throw new ArgumentNullException("password");
Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input);
Pfx bag = new Pfx(obj);
ContentInfo info = bag.AuthSafe;
- bool unmarkedKey = false;
bool wrongPkcs12Zero = false;
- if (bag.MacData != null) // check the mac code
+ if (password != null && bag.MacData != null) // check the mac code
{
MacData mData = bag.MacData;
DigestInfo dInfo = mData.Mac;
@@ -152,8 +233,9 @@ namespace Org.BouncyCastle.Pkcs
keys.Clear();
localIds.Clear();
+ unmarkedKeyEntry = null;
- IList chain = Platform.CreateArrayList();
+ IList certBags = Platform.CreateArrayList();
if (info.ContentType.Equals(PkcsObjectIdentifiers.Data))
{
@@ -166,109 +248,28 @@ namespace Org.BouncyCastle.Pkcs
{
DerObjectIdentifier oid = ci.ContentType;
+ byte[] octets = null;
if (oid.Equals(PkcsObjectIdentifiers.Data))
{
- byte[] octets = ((Asn1OctetString)ci.Content).GetOctets();
- Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets);
-
- foreach (Asn1Sequence subSeq in seq)
+ octets = ((Asn1OctetString)ci.Content).GetOctets();
+ }
+ else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData))
+ {
+ if (password != null)
{
- SafeBag b = new SafeBag(subSeq);
-
- if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
- {
- EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue);
- PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
- password, wrongPkcs12Zero, eIn);
- AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo);
-
- //
- // set the attributes on the key
- //
- IDictionary attributes = Platform.CreateHashtable();
- AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
- string alias = null;
- Asn1OctetString localId = null;
-
- if (b.BagAttributes != null)
- {
- foreach (Asn1Sequence sq in b.BagAttributes)
- {
- DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
- Asn1Set attrSet = (Asn1Set) sq[1];
- Asn1Encodable attr = null;
-
- if (attrSet.Count > 0)
- {
- // TODO We should be adding all attributes in the set
- attr = attrSet[0];
-
- // TODO We might want to "merge" attribute sets with
- // the same OID - currently, differing values give an error
- if (attributes.Contains(aOid.Id))
- {
- // OK, but the value has to be the same
- if (!attributes[aOid.Id].Equals(attr))
- {
- throw new IOException("attempt to add existing attribute with different value");
- }
- }
- else
- {
- attributes.Add(aOid.Id, attr);
- }
-
- if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
- {
- alias = ((DerBmpString)attr).GetString();
- // TODO Do these in a separate loop, just collect aliases here
- keys[alias] = pkcs12Key;
- }
- else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
- {
- localId = (Asn1OctetString)attr;
- }
- }
- }
- }
-
- if (localId != null)
- {
- string name = Hex.ToHexString(localId.GetOctets());
-
- if (alias == null)
- {
- keys[name] = pkcs12Key;
- }
- else
- {
- // TODO There may have been more than one alias
- localIds[alias] = name;
- }
- }
- else
- {
- unmarkedKey = true;
- keys["unmarked"] = pkcs12Key;
- }
- }
- else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
- {
- chain.Add(b);
- }
- else
- {
- Console.WriteLine("extra " + b.BagID);
- Console.WriteLine("extra " + Asn1Dump.DumpAsString(b));
- }
+ EncryptedData d = EncryptedData.GetInstance(ci.Content);
+ octets = CryptPbeData(false, d.EncryptionAlgorithm,
+ password, wrongPkcs12Zero, d.Content.GetOctets());
}
}
- else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData))
+ else
+ {
+ // TODO Other data types
+ }
+
+ if (octets != null)
{
- EncryptedData d = EncryptedData.GetInstance(ci.Content);
- byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm,
- password, wrongPkcs12Zero, d.Content.GetOctets());
- Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets);
+ Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(octets);
foreach (Asn1Sequence subSeq in seq)
{
@@ -276,156 +277,23 @@ namespace Org.BouncyCastle.Pkcs
if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
{
- chain.Add(b);
+ certBags.Add(b);
}
else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
{
- EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue);
- PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
- password, wrongPkcs12Zero, eIn);
- AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo);
-
- //
- // set the attributes on the key
- //
- IDictionary attributes = Platform.CreateHashtable();
- AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
- string alias = null;
- Asn1OctetString localId = null;
-
- foreach (Asn1Sequence sq in b.BagAttributes)
- {
- DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
- Asn1Set attrSet = (Asn1Set) sq[1];
- Asn1Encodable attr = null;
-
- if (attrSet.Count > 0)
- {
- // TODO We should be adding all attributes in the set
- attr = attrSet[0];
-
- // TODO We might want to "merge" attribute sets with
- // the same OID - currently, differing values give an error
- if (attributes.Contains(aOid.Id))
- {
- // OK, but the value has to be the same
- if (!attributes[aOid.Id].Equals(attr))
- {
- throw new IOException("attempt to add existing attribute with different value");
- }
- }
- else
- {
- attributes.Add(aOid.Id, attr);
- }
-
- if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
- {
- alias = ((DerBmpString)attr).GetString();
- // TODO Do these in a separate loop, just collect aliases here
- keys[alias] = pkcs12Key;
- }
- else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
- {
- localId = (Asn1OctetString)attr;
- }
- }
- }
-
- // TODO Should we be checking localIds != null here
- // as for PkcsObjectIdentifiers.Data version above?
-
- string name = Hex.ToHexString(localId.GetOctets());
-
- if (alias == null)
- {
- keys[name] = pkcs12Key;
- }
- else
- {
- // TODO There may have been more than one alias
- localIds[alias] = name;
- }
+ LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo.GetInstance(b.BagValue),
+ b.BagAttributes, password, wrongPkcs12Zero);
}
else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag))
{
- PrivateKeyInfo privKeyInfo = PrivateKeyInfo.GetInstance(b.BagValue);
- AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo);
-
- //
- // set the attributes on the key
- //
- string alias = null;
- Asn1OctetString localId = null;
- IDictionary attributes = Platform.CreateHashtable();
- AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
-
- foreach (Asn1Sequence sq in b.BagAttributes)
- {
- DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]);
- Asn1Set attrSet = Asn1Set.GetInstance(sq[1]);
- Asn1Encodable attr = null;
-
- if (attrSet.Count > 0)
- {
- // TODO We should be adding all attributes in the set
- attr = attrSet[0];
-
- // TODO We might want to "merge" attribute sets with
- // the same OID - currently, differing values give an error
- if (attributes.Contains(aOid.Id))
- {
- // OK, but the value has to be the same
- if (!attributes[aOid.Id].Equals(attr))
- {
- throw new IOException("attempt to add existing attribute with different value");
- }
- }
- else
- {
- attributes.Add(aOid.Id, attr);
- }
-
- if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
- {
- alias = ((DerBmpString)attr).GetString();
- // TODO Do these in a separate loop, just collect aliases here
- keys[alias] = pkcs12Key;
- }
- else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
- {
- localId = (Asn1OctetString)attr;
- }
- }
- }
-
- // TODO Should we be checking localIds != null here
- // as for PkcsObjectIdentifiers.Data version above?
-
- string name = Hex.ToHexString(localId.GetOctets());
-
- if (alias == null)
- {
- keys[name] = pkcs12Key;
- }
- else
- {
- // TODO There may have been more than one alias
- localIds[alias] = name;
- }
+ LoadKeyBag(PrivateKeyInfo.GetInstance(b.BagValue), b.BagAttributes);
}
else
{
- Console.WriteLine("extra " + b.BagID);
- Console.WriteLine("extra " + Asn1Dump.DumpAsString(b));
+ // TODO Other bag types
}
}
}
- else
- {
- Console.WriteLine("extra " + oid);
- Console.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content));
- }
}
}
@@ -433,10 +301,10 @@ namespace Org.BouncyCastle.Pkcs
chainCerts.Clear();
keyCerts.Clear();
- foreach (SafeBag b in chain)
+ foreach (SafeBag b in certBags)
{
- CertBag cb = new CertBag((Asn1Sequence)b.BagValue);
- byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets();
+ CertBag certBag = new CertBag((Asn1Sequence)b.BagValue);
+ byte[] octets = ((Asn1OctetString)certBag.CertValue).GetOctets();
X509Certificate cert = new X509CertificateParser().ReadCertificate(octets);
//
@@ -486,21 +354,18 @@ namespace Org.BouncyCastle.Pkcs
}
CertId certId = new CertId(cert.GetPublicKey());
- X509CertificateEntry pkcs12Cert = new X509CertificateEntry(cert, attributes);
+ X509CertificateEntry certEntry = new X509CertificateEntry(cert, attributes);
- chainCerts[certId] = pkcs12Cert;
+ chainCerts[certId] = certEntry;
- if (unmarkedKey)
+ if (unmarkedKeyEntry != null)
{
if (keyCerts.Count == 0)
{
string name = Hex.ToHexString(certId.Id);
- keyCerts[name] = pkcs12Cert;
-
- object temp = keys["unmarked"];
- keys.Remove("unmarked");
- keys[name] = temp;
+ keyCerts[name] = certEntry;
+ keys[name] = unmarkedKeyEntry;
}
}
else
@@ -509,13 +374,13 @@ namespace Org.BouncyCastle.Pkcs
{
string name = Hex.ToHexString(localId.GetOctets());
- keyCerts[name] = pkcs12Cert;
+ keyCerts[name] = certEntry;
}
if (alias != null)
{
// TODO There may have been more than one alias
- certs[alias] = pkcs12Cert;
+ certs[alias] = certEntry;
}
}
}
@@ -841,24 +706,34 @@ namespace Org.BouncyCastle.Pkcs
{
if (stream == null)
throw new ArgumentNullException("stream");
- if (password == null)
- throw new ArgumentNullException("password");
if (random == null)
throw new ArgumentNullException("random");
//
- // handle the key
+ // handle the keys
//
- Asn1EncodableVector keyS = new Asn1EncodableVector();
+ Asn1EncodableVector keyBags = new Asn1EncodableVector();
foreach (string name in keys.Keys)
{
byte[] kSalt = new byte[SaltSize];
random.NextBytes(kSalt);
- AsymmetricKeyEntry privKey = (AsymmetricKeyEntry) keys[name];
- EncryptedPrivateKeyInfo kInfo =
- EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
- keyAlgorithm, password, kSalt, MinIterations, privKey.Key);
+ AsymmetricKeyEntry privKey = (AsymmetricKeyEntry)keys[name];
+
+ DerObjectIdentifier bagOid;
+ Asn1Encodable bagData;
+
+ if (password == null)
+ {
+ bagOid = PkcsObjectIdentifiers.KeyBag;
+ bagData = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey.Key);
+ }
+ else
+ {
+ bagOid = PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag;
+ bagData = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
+ keyAlgorithm, password, kSalt, MinIterations, privKey.Key);
+ }
Asn1EncodableVector kName = new Asn1EncodableVector();
@@ -903,13 +778,11 @@ namespace Org.BouncyCastle.Pkcs
new DerSet(subjectKeyID)));
}
- SafeBag kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName));
- keyS.Add(kBag);
+ keyBags.Add(new SafeBag(bagOid, bagData.ToAsn1Object(), new DerSet(kName)));
}
- byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded();
-
- BerOctetString keyString = new BerOctetString(derEncodedBytes);
+ byte[] keyBagsEncoding = new DerSequence(keyBags).GetDerEncoded();
+ ContentInfo keysInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(keyBagsEncoding));
//
// certificate processing
@@ -918,7 +791,7 @@ namespace Org.BouncyCastle.Pkcs
random.NextBytes(cSalt);
- Asn1EncodableVector certSeq = new Asn1EncodableVector();
+ Asn1EncodableVector certBags = new Asn1EncodableVector();
Pkcs12PbeParams cParams = new Pkcs12PbeParams(cSalt, MinIterations);
AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object());
ISet doneCerts = new HashSet();
@@ -972,10 +845,7 @@ namespace Org.BouncyCastle.Pkcs
new DerSet(subjectKeyID)));
}
- SafeBag sBag = new SafeBag(
- PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));
-
- certSeq.Add(sBag);
+ certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
doneCerts.Add(certEntry.Certificate);
}
@@ -1026,10 +896,7 @@ namespace Org.BouncyCastle.Pkcs
new DerSet(new DerBmpString(certId))));
}
- SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag,
- cBag.ToAsn1Object(), new DerSet(fName));
-
- certSeq.Add(sBag);
+ certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
doneCerts.Add(cert.Certificate);
}
@@ -1062,22 +929,24 @@ namespace Org.BouncyCastle.Pkcs
new DerSet(cert[oid])));
}
- SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));
-
- certSeq.Add(sBag);
+ certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
}
- derEncodedBytes = new DerSequence(certSeq).GetDerEncoded();
-
- byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes);
+ byte[] certBagsEncoding = new DerSequence(certBags).GetDerEncoded();
- EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes));
-
- ContentInfo[] info = new ContentInfo[]
+ ContentInfo certsInfo;
+ if (password == null)
{
- new ContentInfo(PkcsObjectIdentifiers.Data, keyString),
- new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object())
- };
+ certsInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(certBagsEncoding));
+ }
+ else
+ {
+ byte[] certBytes = CryptPbeData(true, cAlgId, password, false, certBagsEncoding);
+ EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes));
+ certsInfo = new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object());
+ }
+
+ ContentInfo[] info = new ContentInfo[]{ keysInfo, certsInfo };
byte[] data = new AuthenticatedSafe(info).GetEncoded(
useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);
@@ -1087,22 +956,26 @@ namespace Org.BouncyCastle.Pkcs
//
// create the mac
//
- byte[] mSalt = new byte[20];
- random.NextBytes(mSalt);
+ MacData macData = null;
+ if (password != null)
+ {
+ byte[] mSalt = new byte[20];
+ random.NextBytes(mSalt);
- byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1,
- mSalt, MinIterations, password, false, data);
+ byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1,
+ mSalt, MinIterations, password, false, data);
- AlgorithmIdentifier algId = new AlgorithmIdentifier(
- OiwObjectIdentifiers.IdSha1, DerNull.Instance);
- DigestInfo dInfo = new DigestInfo(algId, mac);
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(
+ OiwObjectIdentifiers.IdSha1, DerNull.Instance);
+ DigestInfo dInfo = new DigestInfo(algId, mac);
- MacData mData = new MacData(dInfo, mSalt, MinIterations);
+ macData = new MacData(dInfo, mSalt, MinIterations);
+ }
//
// output the Pfx
//
- Pfx pfx = new Pfx(mainInfo, mData);
+ Pfx pfx = new Pfx(mainInfo, macData);
DerOutputStream derOut;
if (useDerEncoding)
diff --git a/crypto/src/security/PrivateKeyFactory.cs b/crypto/src/security/PrivateKeyFactory.cs
index 1cfa37afe..edc5ef85a 100644
--- a/crypto/src/security/PrivateKeyFactory.cs
+++ b/crypto/src/security/PrivateKeyFactory.cs
@@ -53,8 +53,7 @@ namespace Org.BouncyCastle.Security
|| algOid.Equals(PkcsObjectIdentifiers.IdRsassaPss)
|| algOid.Equals(PkcsObjectIdentifiers.IdRsaesOaep))
{
- RsaPrivateKeyStructure keyStructure = new RsaPrivateKeyStructure(
- Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey()));
+ RsaPrivateKeyStructure keyStructure = RsaPrivateKeyStructure.GetInstance(keyInfo.ParsePrivateKey());
return new RsaPrivateCrtKeyParameters(
keyStructure.Modulus,
diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index 136361532..0cf113f65 100644
--- a/crypto/src/security/SignerUtilities.cs
+++ b/crypto/src/security/SignerUtilities.cs
@@ -23,16 +23,16 @@ namespace Org.BouncyCastle.Security
/// </summary>
public sealed class SignerUtilities
{
- private SignerUtilities()
- {
- }
+ private SignerUtilities()
+ {
+ }
- internal static readonly IDictionary algorithms = Platform.CreateHashtable();
+ internal static readonly IDictionary algorithms = Platform.CreateHashtable();
internal static readonly IDictionary oids = Platform.CreateHashtable();
- static SignerUtilities()
+ static SignerUtilities()
{
- algorithms["MD2WITHRSA"] = "MD2withRSA";
+ algorithms["MD2WITHRSA"] = "MD2withRSA";
algorithms["MD2WITHRSAENCRYPTION"] = "MD2withRSA";
algorithms[PkcsObjectIdentifiers.MD2WithRsaEncryption.Id] = "MD2withRSA";
@@ -69,41 +69,41 @@ namespace Org.BouncyCastle.Security
algorithms[PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id] = "SHA-512withRSA";
algorithms["SHA-512WITHRSA"] = "SHA-512withRSA";
- algorithms["PSSWITHRSA"] = "PSSwithRSA";
- algorithms["RSASSA-PSS"] = "PSSwithRSA";
- algorithms[PkcsObjectIdentifiers.IdRsassaPss.Id] = "PSSwithRSA";
- algorithms["RSAPSS"] = "PSSwithRSA";
+ algorithms["PSSWITHRSA"] = "PSSwithRSA";
+ algorithms["RSASSA-PSS"] = "PSSwithRSA";
+ algorithms[PkcsObjectIdentifiers.IdRsassaPss.Id] = "PSSwithRSA";
+ algorithms["RSAPSS"] = "PSSwithRSA";
- algorithms["SHA1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1";
- algorithms["SHA-1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1";
- algorithms["SHA1WITHRSA/PSS"] = "SHA-1withRSAandMGF1";
- algorithms["SHA-1WITHRSA/PSS"] = "SHA-1withRSAandMGF1";
+ algorithms["SHA1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1";
+ algorithms["SHA-1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1";
+ algorithms["SHA1WITHRSA/PSS"] = "SHA-1withRSAandMGF1";
+ algorithms["SHA-1WITHRSA/PSS"] = "SHA-1withRSAandMGF1";
- algorithms["SHA224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1";
- algorithms["SHA-224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1";
- algorithms["SHA224WITHRSA/PSS"] = "SHA-224withRSAandMGF1";
- algorithms["SHA-224WITHRSA/PSS"] = "SHA-224withRSAandMGF1";
+ algorithms["SHA224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1";
+ algorithms["SHA-224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1";
+ algorithms["SHA224WITHRSA/PSS"] = "SHA-224withRSAandMGF1";
+ algorithms["SHA-224WITHRSA/PSS"] = "SHA-224withRSAandMGF1";
- algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1";
+ algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1";
algorithms["SHA-256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1";
- algorithms["SHA256WITHRSA/PSS"] = "SHA-256withRSAandMGF1";
- algorithms["SHA-256WITHRSA/PSS"] = "SHA-256withRSAandMGF1";
+ algorithms["SHA256WITHRSA/PSS"] = "SHA-256withRSAandMGF1";
+ algorithms["SHA-256WITHRSA/PSS"] = "SHA-256withRSAandMGF1";
algorithms["SHA384WITHRSAANDMGF1"] = "SHA-384withRSAandMGF1";
algorithms["SHA-384WITHRSAANDMGF1"] = "SHA-384withRSAandMGF1";
- algorithms["SHA384WITHRSA/PSS"] = "SHA-384withRSAandMGF1";
- algorithms["SHA-384WITHRSA/PSS"] = "SHA-384withRSAandMGF1";
+ algorithms["SHA384WITHRSA/PSS"] = "SHA-384withRSAandMGF1";
+ algorithms["SHA-384WITHRSA/PSS"] = "SHA-384withRSAandMGF1";
algorithms["SHA512WITHRSAANDMGF1"] = "SHA-512withRSAandMGF1";
algorithms["SHA-512WITHRSAANDMGF1"] = "SHA-512withRSAandMGF1";
- algorithms["SHA512WITHRSA/PSS"] = "SHA-512withRSAandMGF1";
- algorithms["SHA-512WITHRSA/PSS"] = "SHA-512withRSAandMGF1";
+ algorithms["SHA512WITHRSA/PSS"] = "SHA-512withRSAandMGF1";
+ algorithms["SHA-512WITHRSA/PSS"] = "SHA-512withRSAandMGF1";
- algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA";
+ algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA";
algorithms["RIPEMD128WITHRSAENCRYPTION"] = "RIPEMD128withRSA";
algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128.Id] = "RIPEMD128withRSA";
- algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA";
+ algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA";
algorithms["RIPEMD160WITHRSAENCRYPTION"] = "RIPEMD160withRSA";
algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160.Id] = "RIPEMD160withRSA";
@@ -111,126 +111,123 @@ namespace Org.BouncyCastle.Security
algorithms["RIPEMD256WITHRSAENCRYPTION"] = "RIPEMD256withRSA";
algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256.Id] = "RIPEMD256withRSA";
- algorithms["NONEWITHRSA"] = "RSA";
- algorithms["RSAWITHNONE"] = "RSA";
- algorithms["RAWRSA"] = "RSA";
-
- algorithms["RAWRSAPSS"] = "RAWRSASSA-PSS";
- algorithms["NONEWITHRSAPSS"] = "RAWRSASSA-PSS";
- algorithms["NONEWITHRSASSA-PSS"] = "RAWRSASSA-PSS";
-
- algorithms["NONEWITHDSA"] = "NONEwithDSA";
- algorithms["DSAWITHNONE"] = "NONEwithDSA";
- algorithms["RAWDSA"] = "NONEwithDSA";
-
- algorithms["DSA"] = "SHA-1withDSA";
- algorithms["DSAWITHSHA1"] = "SHA-1withDSA";
- algorithms["DSAWITHSHA-1"] = "SHA-1withDSA";
- algorithms["SHA/DSA"] = "SHA-1withDSA";
- algorithms["SHA1/DSA"] = "SHA-1withDSA";
- algorithms["SHA-1/DSA"] = "SHA-1withDSA";
- algorithms["SHA1WITHDSA"] = "SHA-1withDSA";
+ algorithms["NONEWITHRSA"] = "RSA";
+ algorithms["RSAWITHNONE"] = "RSA";
+ algorithms["RAWRSA"] = "RSA";
+
+ algorithms["RAWRSAPSS"] = "RAWRSASSA-PSS";
+ algorithms["NONEWITHRSAPSS"] = "RAWRSASSA-PSS";
+ algorithms["NONEWITHRSASSA-PSS"] = "RAWRSASSA-PSS";
+
+ algorithms["NONEWITHDSA"] = "NONEwithDSA";
+ algorithms["DSAWITHNONE"] = "NONEwithDSA";
+ algorithms["RAWDSA"] = "NONEwithDSA";
+
+ algorithms["DSA"] = "SHA-1withDSA";
+ algorithms["DSAWITHSHA1"] = "SHA-1withDSA";
+ algorithms["DSAWITHSHA-1"] = "SHA-1withDSA";
+ algorithms["SHA/DSA"] = "SHA-1withDSA";
+ algorithms["SHA1/DSA"] = "SHA-1withDSA";
+ algorithms["SHA-1/DSA"] = "SHA-1withDSA";
+ algorithms["SHA1WITHDSA"] = "SHA-1withDSA";
algorithms["SHA-1WITHDSA"] = "SHA-1withDSA";
algorithms[X9ObjectIdentifiers.IdDsaWithSha1.Id] = "SHA-1withDSA";
- algorithms["DSAWITHSHA224"] = "SHA-224withDSA";
- algorithms["DSAWITHSHA-224"] = "SHA-224withDSA";
- algorithms["SHA224/DSA"] = "SHA-224withDSA";
- algorithms["SHA-224/DSA"] = "SHA-224withDSA";
- algorithms["SHA224WITHDSA"] = "SHA-224withDSA";
- algorithms["SHA-224WITHDSA"] = "SHA-224withDSA";
- algorithms[NistObjectIdentifiers.DsaWithSha224.Id] = "SHA-224withDSA";
-
- algorithms["DSAWITHSHA256"] = "SHA-256withDSA";
- algorithms["DSAWITHSHA-256"] = "SHA-256withDSA";
- algorithms["SHA256/DSA"] = "SHA-256withDSA";
- algorithms["SHA-256/DSA"] = "SHA-256withDSA";
- algorithms["SHA256WITHDSA"] = "SHA-256withDSA";
- algorithms["SHA-256WITHDSA"] = "SHA-256withDSA";
- algorithms[NistObjectIdentifiers.DsaWithSha256.Id] = "SHA-256withDSA";
-
- algorithms["DSAWITHSHA384"] = "SHA-384withDSA";
- algorithms["DSAWITHSHA-384"] = "SHA-384withDSA";
- algorithms["SHA384/DSA"] = "SHA-384withDSA";
- algorithms["SHA-384/DSA"] = "SHA-384withDSA";
- algorithms["SHA384WITHDSA"] = "SHA-384withDSA";
- algorithms["SHA-384WITHDSA"] = "SHA-384withDSA";
- algorithms[NistObjectIdentifiers.DsaWithSha384.Id] = "SHA-384withDSA";
-
- algorithms["DSAWITHSHA512"] = "SHA-512withDSA";
- algorithms["DSAWITHSHA-512"] = "SHA-512withDSA";
- algorithms["SHA512/DSA"] = "SHA-512withDSA";
- algorithms["SHA-512/DSA"] = "SHA-512withDSA";
- algorithms["SHA512WITHDSA"] = "SHA-512withDSA";
- algorithms["SHA-512WITHDSA"] = "SHA-512withDSA";
- algorithms[NistObjectIdentifiers.DsaWithSha512.Id] = "SHA-512withDSA";
-
- algorithms["NONEWITHECDSA"] = "NONEwithECDSA";
- algorithms["ECDSAWITHNONE"] = "NONEwithECDSA";
-
- algorithms["ECDSA"] = "SHA-1withECDSA";
- algorithms["SHA1/ECDSA"] = "SHA-1withECDSA";
- algorithms["SHA-1/ECDSA"] = "SHA-1withECDSA";
- algorithms["ECDSAWITHSHA1"] = "SHA-1withECDSA";
- algorithms["ECDSAWITHSHA-1"] = "SHA-1withECDSA";
- algorithms["SHA1WITHECDSA"] = "SHA-1withECDSA";
+ algorithms["DSAWITHSHA224"] = "SHA-224withDSA";
+ algorithms["DSAWITHSHA-224"] = "SHA-224withDSA";
+ algorithms["SHA224/DSA"] = "SHA-224withDSA";
+ algorithms["SHA-224/DSA"] = "SHA-224withDSA";
+ algorithms["SHA224WITHDSA"] = "SHA-224withDSA";
+ algorithms["SHA-224WITHDSA"] = "SHA-224withDSA";
+ algorithms[NistObjectIdentifiers.DsaWithSha224.Id] = "SHA-224withDSA";
+
+ algorithms["DSAWITHSHA256"] = "SHA-256withDSA";
+ algorithms["DSAWITHSHA-256"] = "SHA-256withDSA";
+ algorithms["SHA256/DSA"] = "SHA-256withDSA";
+ algorithms["SHA-256/DSA"] = "SHA-256withDSA";
+ algorithms["SHA256WITHDSA"] = "SHA-256withDSA";
+ algorithms["SHA-256WITHDSA"] = "SHA-256withDSA";
+ algorithms[NistObjectIdentifiers.DsaWithSha256.Id] = "SHA-256withDSA";
+
+ algorithms["DSAWITHSHA384"] = "SHA-384withDSA";
+ algorithms["DSAWITHSHA-384"] = "SHA-384withDSA";
+ algorithms["SHA384/DSA"] = "SHA-384withDSA";
+ algorithms["SHA-384/DSA"] = "SHA-384withDSA";
+ algorithms["SHA384WITHDSA"] = "SHA-384withDSA";
+ algorithms["SHA-384WITHDSA"] = "SHA-384withDSA";
+ algorithms[NistObjectIdentifiers.DsaWithSha384.Id] = "SHA-384withDSA";
+
+ algorithms["DSAWITHSHA512"] = "SHA-512withDSA";
+ algorithms["DSAWITHSHA-512"] = "SHA-512withDSA";
+ algorithms["SHA512/DSA"] = "SHA-512withDSA";
+ algorithms["SHA-512/DSA"] = "SHA-512withDSA";
+ algorithms["SHA512WITHDSA"] = "SHA-512withDSA";
+ algorithms["SHA-512WITHDSA"] = "SHA-512withDSA";
+ algorithms[NistObjectIdentifiers.DsaWithSha512.Id] = "SHA-512withDSA";
+
+ algorithms["NONEWITHECDSA"] = "NONEwithECDSA";
+ algorithms["ECDSAWITHNONE"] = "NONEwithECDSA";
+
+ algorithms["ECDSA"] = "SHA-1withECDSA";
+ algorithms["SHA1/ECDSA"] = "SHA-1withECDSA";
+ algorithms["SHA-1/ECDSA"] = "SHA-1withECDSA";
+ algorithms["ECDSAWITHSHA1"] = "SHA-1withECDSA";
+ algorithms["ECDSAWITHSHA-1"] = "SHA-1withECDSA";
+ algorithms["SHA1WITHECDSA"] = "SHA-1withECDSA";
algorithms["SHA-1WITHECDSA"] = "SHA-1withECDSA";
- algorithms[X9ObjectIdentifiers.ECDsaWithSha1.Id] = "SHA-1withECDSA";
- algorithms[TeleTrusTObjectIdentifiers.ECSignWithSha1.Id] = "SHA-1withECDSA";
-
- algorithms["SHA224/ECDSA"] = "SHA-224withECDSA";
- algorithms["SHA-224/ECDSA"] = "SHA-224withECDSA";
- algorithms["ECDSAWITHSHA224"] = "SHA-224withECDSA";
- algorithms["ECDSAWITHSHA-224"] = "SHA-224withECDSA";
- algorithms["SHA224WITHECDSA"] = "SHA-224withECDSA";
- algorithms["SHA-224WITHECDSA"] = "SHA-224withECDSA";
- algorithms[X9ObjectIdentifiers.ECDsaWithSha224.Id] = "SHA-224withECDSA";
-
- algorithms["SHA256/ECDSA"] = "SHA-256withECDSA";
- algorithms["SHA-256/ECDSA"] = "SHA-256withECDSA";
- algorithms["ECDSAWITHSHA256"] = "SHA-256withECDSA";
- algorithms["ECDSAWITHSHA-256"] = "SHA-256withECDSA";
- algorithms["SHA256WITHECDSA"] = "SHA-256withECDSA";
- algorithms["SHA-256WITHECDSA"] = "SHA-256withECDSA";
- algorithms[X9ObjectIdentifiers.ECDsaWithSha256.Id] = "SHA-256withECDSA";
-
- algorithms["SHA384/ECDSA"] = "SHA-384withECDSA";
- algorithms["SHA-384/ECDSA"] = "SHA-384withECDSA";
- algorithms["ECDSAWITHSHA384"] = "SHA-384withECDSA";
- algorithms["ECDSAWITHSHA-384"] = "SHA-384withECDSA";
- algorithms["SHA384WITHECDSA"] = "SHA-384withECDSA";
- algorithms["SHA-384WITHECDSA"] = "SHA-384withECDSA";
- algorithms[X9ObjectIdentifiers.ECDsaWithSha384.Id] = "SHA-384withECDSA";
-
- algorithms["SHA512/ECDSA"] = "SHA-512withECDSA";
- algorithms["SHA-512/ECDSA"] = "SHA-512withECDSA";
- algorithms["ECDSAWITHSHA512"] = "SHA-512withECDSA";
- algorithms["ECDSAWITHSHA-512"] = "SHA-512withECDSA";
- algorithms["SHA512WITHECDSA"] = "SHA-512withECDSA";
- algorithms["SHA-512WITHECDSA"] = "SHA-512withECDSA";
- algorithms[X9ObjectIdentifiers.ECDsaWithSha512.Id] = "SHA-512withECDSA";
-
- algorithms["RIPEMD160/ECDSA"] = "RIPEMD160withECDSA";
- algorithms["SHA-512/ECDSA"] = "RIPEMD160withECDSA";
- algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA";
- algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA";
- algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA";
- algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA";
- algorithms[TeleTrusTObjectIdentifiers.ECSignWithRipeMD160.Id] = "RIPEMD160withECDSA";
-
- algorithms["GOST-3410"] = "GOST3410";
- algorithms["GOST-3410-94"] = "GOST3410";
- algorithms["GOST3411WITHGOST3410"] = "GOST3410";
- algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94.Id] = "GOST3410";
-
- algorithms["ECGOST-3410"] = "ECGOST3410";
- algorithms["ECGOST-3410-2001"] = "ECGOST3410";
- algorithms["GOST3411WITHECGOST3410"] = "ECGOST3410";
- algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001.Id] = "ECGOST3410";
-
-
-
- oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption;
+ algorithms[X9ObjectIdentifiers.ECDsaWithSha1.Id] = "SHA-1withECDSA";
+ algorithms[TeleTrusTObjectIdentifiers.ECSignWithSha1.Id] = "SHA-1withECDSA";
+
+ algorithms["SHA224/ECDSA"] = "SHA-224withECDSA";
+ algorithms["SHA-224/ECDSA"] = "SHA-224withECDSA";
+ algorithms["ECDSAWITHSHA224"] = "SHA-224withECDSA";
+ algorithms["ECDSAWITHSHA-224"] = "SHA-224withECDSA";
+ algorithms["SHA224WITHECDSA"] = "SHA-224withECDSA";
+ algorithms["SHA-224WITHECDSA"] = "SHA-224withECDSA";
+ algorithms[X9ObjectIdentifiers.ECDsaWithSha224.Id] = "SHA-224withECDSA";
+
+ algorithms["SHA256/ECDSA"] = "SHA-256withECDSA";
+ algorithms["SHA-256/ECDSA"] = "SHA-256withECDSA";
+ algorithms["ECDSAWITHSHA256"] = "SHA-256withECDSA";
+ algorithms["ECDSAWITHSHA-256"] = "SHA-256withECDSA";
+ algorithms["SHA256WITHECDSA"] = "SHA-256withECDSA";
+ algorithms["SHA-256WITHECDSA"] = "SHA-256withECDSA";
+ algorithms[X9ObjectIdentifiers.ECDsaWithSha256.Id] = "SHA-256withECDSA";
+
+ algorithms["SHA384/ECDSA"] = "SHA-384withECDSA";
+ algorithms["SHA-384/ECDSA"] = "SHA-384withECDSA";
+ algorithms["ECDSAWITHSHA384"] = "SHA-384withECDSA";
+ algorithms["ECDSAWITHSHA-384"] = "SHA-384withECDSA";
+ algorithms["SHA384WITHECDSA"] = "SHA-384withECDSA";
+ algorithms["SHA-384WITHECDSA"] = "SHA-384withECDSA";
+ algorithms[X9ObjectIdentifiers.ECDsaWithSha384.Id] = "SHA-384withECDSA";
+
+ algorithms["SHA512/ECDSA"] = "SHA-512withECDSA";
+ algorithms["SHA-512/ECDSA"] = "SHA-512withECDSA";
+ algorithms["ECDSAWITHSHA512"] = "SHA-512withECDSA";
+ algorithms["ECDSAWITHSHA-512"] = "SHA-512withECDSA";
+ algorithms["SHA512WITHECDSA"] = "SHA-512withECDSA";
+ algorithms["SHA-512WITHECDSA"] = "SHA-512withECDSA";
+ algorithms[X9ObjectIdentifiers.ECDsaWithSha512.Id] = "SHA-512withECDSA";
+
+ algorithms["RIPEMD160/ECDSA"] = "RIPEMD160withECDSA";
+ algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA";
+ algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA";
+ algorithms[TeleTrusTObjectIdentifiers.ECSignWithRipeMD160.Id] = "RIPEMD160withECDSA";
+
+ algorithms["GOST-3410"] = "GOST3410";
+ algorithms["GOST-3410-94"] = "GOST3410";
+ algorithms["GOST3411WITHGOST3410"] = "GOST3410";
+ algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94.Id] = "GOST3410";
+
+ algorithms["ECGOST-3410"] = "ECGOST3410";
+ algorithms["ECGOST-3410-2001"] = "ECGOST3410";
+ algorithms["GOST3411WITHECGOST3410"] = "ECGOST3410";
+ algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001.Id] = "ECGOST3410";
+
+
+
+ oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption;
oids["MD4withRSA"] = PkcsObjectIdentifiers.MD4WithRsaEncryption;
oids["MD5withRSA"] = PkcsObjectIdentifiers.MD5WithRsaEncryption;
@@ -240,129 +237,129 @@ namespace Org.BouncyCastle.Security
oids["SHA-384withRSA"] = PkcsObjectIdentifiers.Sha384WithRsaEncryption;
oids["SHA-512withRSA"] = PkcsObjectIdentifiers.Sha512WithRsaEncryption;
- oids["PSSwithRSA"] = PkcsObjectIdentifiers.IdRsassaPss;
- oids["SHA-1withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
- oids["SHA-224withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
- oids["SHA-256withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
- oids["SHA-384withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
- oids["SHA-512withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
+ oids["PSSwithRSA"] = PkcsObjectIdentifiers.IdRsassaPss;
+ oids["SHA-1withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
+ oids["SHA-224withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
+ oids["SHA-256withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
+ oids["SHA-384withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
+ oids["SHA-512withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss;
- oids["RIPEMD128withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128;
- oids["RIPEMD160withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160;
- oids["RIPEMD256withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256;
+ oids["RIPEMD128withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128;
+ oids["RIPEMD160withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160;
+ oids["RIPEMD256withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256;
- oids["SHA-1withDSA"] = X9ObjectIdentifiers.IdDsaWithSha1;
+ oids["SHA-1withDSA"] = X9ObjectIdentifiers.IdDsaWithSha1;
- oids["SHA-1withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha1;
- oids["SHA-224withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha224;
- oids["SHA-256withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha256;
- oids["SHA-384withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha384;
- oids["SHA-512withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha512;
+ oids["SHA-1withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha1;
+ oids["SHA-224withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha224;
+ oids["SHA-256withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha256;
+ oids["SHA-384withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha384;
+ oids["SHA-512withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha512;
- oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94;
- oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001;
- }
+ oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94;
+ oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001;
+ }
- /// <summary>
+ /// <summary>
/// Returns a ObjectIdentifier for a give encoding.
/// </summary>
/// <param name="mechanism">A string representation of the encoding.</param>
/// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns>
- // TODO Don't really want to support this
+ // TODO Don't really want to support this
public static DerObjectIdentifier GetObjectIdentifier(
- string mechanism)
+ string mechanism)
{
- if (mechanism == null)
- throw new ArgumentNullException("mechanism");
+ if (mechanism == null)
+ throw new ArgumentNullException("mechanism");
- mechanism = Platform.ToUpperInvariant(mechanism);
- string aliased = (string) algorithms[mechanism];
+ mechanism = Platform.ToUpperInvariant(mechanism);
+ string aliased = (string) algorithms[mechanism];
- if (aliased != null)
- mechanism = aliased;
+ if (aliased != null)
+ mechanism = aliased;
- return (DerObjectIdentifier) oids[mechanism];
- }
+ return (DerObjectIdentifier) oids[mechanism];
+ }
- public static ICollection Algorithms
+ public static ICollection Algorithms
{
get { return oids.Keys; }
}
- public static Asn1Encodable GetDefaultX509Parameters(
- DerObjectIdentifier id)
- {
- return GetDefaultX509Parameters(id.Id);
- }
-
- public static Asn1Encodable GetDefaultX509Parameters(
- string algorithm)
- {
- if (algorithm == null)
- throw new ArgumentNullException("algorithm");
-
- algorithm = Platform.ToUpperInvariant(algorithm);
-
- string mechanism = (string) algorithms[algorithm];
-
- if (mechanism == null)
- mechanism = algorithm;
-
- if (mechanism == "PSSwithRSA")
- {
- // TODO The Sha1Digest here is a default. In JCE version, the actual digest
- // to be used can be overridden by subsequent parameter settings.
- return GetPssX509Parameters("SHA-1");
- }
-
- if (mechanism.EndsWith("withRSAandMGF1"))
- {
- string digestName = mechanism.Substring(0, mechanism.Length - "withRSAandMGF1".Length);
- return GetPssX509Parameters(digestName);
- }
-
- return DerNull.Instance;
- }
-
- private static Asn1Encodable GetPssX509Parameters(
- string digestName)
- {
- AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
- DigestUtilities.GetObjectIdentifier(digestName), DerNull.Instance);
-
- // TODO Is it possible for the MGF hash alg to be different from the PSS one?
- AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
- PkcsObjectIdentifiers.IdMgf1, hashAlgorithm);
-
- int saltLen = DigestUtilities.GetDigest(digestName).GetDigestSize();
- return new RsassaPssParameters(hashAlgorithm, maskGenAlgorithm,
- new DerInteger(saltLen), new DerInteger(1));
- }
-
- public static ISigner GetSigner(
- DerObjectIdentifier id)
+ public static Asn1Encodable GetDefaultX509Parameters(
+ DerObjectIdentifier id)
+ {
+ return GetDefaultX509Parameters(id.Id);
+ }
+
+ public static Asn1Encodable GetDefaultX509Parameters(
+ string algorithm)
+ {
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+
+ algorithm = Platform.ToUpperInvariant(algorithm);
+
+ string mechanism = (string) algorithms[algorithm];
+
+ if (mechanism == null)
+ mechanism = algorithm;
+
+ if (mechanism == "PSSwithRSA")
+ {
+ // TODO The Sha1Digest here is a default. In JCE version, the actual digest
+ // to be used can be overridden by subsequent parameter settings.
+ return GetPssX509Parameters("SHA-1");
+ }
+
+ if (mechanism.EndsWith("withRSAandMGF1"))
+ {
+ string digestName = mechanism.Substring(0, mechanism.Length - "withRSAandMGF1".Length);
+ return GetPssX509Parameters(digestName);
+ }
+
+ return DerNull.Instance;
+ }
+
+ private static Asn1Encodable GetPssX509Parameters(
+ string digestName)
+ {
+ AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
+ DigestUtilities.GetObjectIdentifier(digestName), DerNull.Instance);
+
+ // TODO Is it possible for the MGF hash alg to be different from the PSS one?
+ AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+ PkcsObjectIdentifiers.IdMgf1, hashAlgorithm);
+
+ int saltLen = DigestUtilities.GetDigest(digestName).GetDigestSize();
+ return new RsassaPssParameters(hashAlgorithm, maskGenAlgorithm,
+ new DerInteger(saltLen), new DerInteger(1));
+ }
+
+ public static ISigner GetSigner(
+ DerObjectIdentifier id)
{
return GetSigner(id.Id);
}
- public static ISigner GetSigner(
- string algorithm)
+ public static ISigner GetSigner(
+ string algorithm)
{
- if (algorithm == null)
- throw new ArgumentNullException("algorithm");
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
algorithm = Platform.ToUpperInvariant(algorithm);
- string mechanism = (string) algorithms[algorithm];
+ string mechanism = (string) algorithms[algorithm];
- if (mechanism == null)
- mechanism = algorithm;
+ if (mechanism == null)
+ mechanism = algorithm;
- if (mechanism.Equals("RSA"))
- {
- return (new RsaDigestSigner(new NullDigest()));
- }
- if (mechanism.Equals("MD2withRSA"))
+ if (mechanism.Equals("RSA"))
+ {
+ return (new RsaDigestSigner(new NullDigest(), (AlgorithmIdentifier)null));
+ }
+ if (mechanism.Equals("MD2withRSA"))
{
return (new RsaDigestSigner(new MD2Digest()));
}
@@ -394,7 +391,7 @@ namespace Org.BouncyCastle.Security
{
return (new RsaDigestSigner(new Sha512Digest()));
}
- if (mechanism.Equals("RIPEMD128withRSA"))
+ if (mechanism.Equals("RIPEMD128withRSA"))
{
return (new RsaDigestSigner(new RipeMD128Digest()));
}
@@ -407,141 +404,141 @@ namespace Org.BouncyCastle.Security
return (new RsaDigestSigner(new RipeMD256Digest()));
}
- if (mechanism.Equals("RAWRSASSA-PSS"))
- {
- // TODO Add support for other parameter settings
- return PssSigner.CreateRawSigner(new RsaBlindedEngine(), new Sha1Digest());
- }
- if (mechanism.Equals("PSSwithRSA"))
- {
- // TODO The Sha1Digest here is a default. In JCE version, the actual digest
- // to be used can be overridden by subsequent parameter settings.
- return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest()));
- }
- if (mechanism.Equals("SHA-1withRSAandMGF1"))
- {
- return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest()));
- }
- if (mechanism.Equals("SHA-224withRSAandMGF1"))
- {
- return (new PssSigner(new RsaBlindedEngine(), new Sha224Digest()));
- }
- if (mechanism.Equals("SHA-256withRSAandMGF1"))
- {
- return (new PssSigner(new RsaBlindedEngine(), new Sha256Digest()));
- }
- if (mechanism.Equals("SHA-384withRSAandMGF1"))
- {
- return (new PssSigner(new RsaBlindedEngine(), new Sha384Digest()));
- }
- if (mechanism.Equals("SHA-512withRSAandMGF1"))
- {
- return (new PssSigner(new RsaBlindedEngine(), new Sha512Digest()));
- }
-
- if (mechanism.Equals("NONEwithDSA"))
- {
- return (new DsaDigestSigner(new DsaSigner(), new NullDigest()));
- }
- if (mechanism.Equals("SHA-1withDSA"))
+ if (mechanism.Equals("RAWRSASSA-PSS"))
+ {
+ // TODO Add support for other parameter settings
+ return PssSigner.CreateRawSigner(new RsaBlindedEngine(), new Sha1Digest());
+ }
+ if (mechanism.Equals("PSSwithRSA"))
+ {
+ // TODO The Sha1Digest here is a default. In JCE version, the actual digest
+ // to be used can be overridden by subsequent parameter settings.
+ return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest()));
+ }
+ if (mechanism.Equals("SHA-1withRSAandMGF1"))
+ {
+ return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest()));
+ }
+ if (mechanism.Equals("SHA-224withRSAandMGF1"))
+ {
+ return (new PssSigner(new RsaBlindedEngine(), new Sha224Digest()));
+ }
+ if (mechanism.Equals("SHA-256withRSAandMGF1"))
+ {
+ return (new PssSigner(new RsaBlindedEngine(), new Sha256Digest()));
+ }
+ if (mechanism.Equals("SHA-384withRSAandMGF1"))
+ {
+ return (new PssSigner(new RsaBlindedEngine(), new Sha384Digest()));
+ }
+ if (mechanism.Equals("SHA-512withRSAandMGF1"))
+ {
+ return (new PssSigner(new RsaBlindedEngine(), new Sha512Digest()));
+ }
+
+ if (mechanism.Equals("NONEwithDSA"))
+ {
+ return (new DsaDigestSigner(new DsaSigner(), new NullDigest()));
+ }
+ if (mechanism.Equals("SHA-1withDSA"))
{
return (new DsaDigestSigner(new DsaSigner(), new Sha1Digest()));
}
- if (mechanism.Equals("SHA-224withDSA"))
- {
- return (new DsaDigestSigner(new DsaSigner(), new Sha224Digest()));
- }
- if (mechanism.Equals("SHA-256withDSA"))
- {
- return (new DsaDigestSigner(new DsaSigner(), new Sha256Digest()));
- }
- if (mechanism.Equals("SHA-384withDSA"))
- {
- return (new DsaDigestSigner(new DsaSigner(), new Sha384Digest()));
- }
- if (mechanism.Equals("SHA-512withDSA"))
- {
- return (new DsaDigestSigner(new DsaSigner(), new Sha512Digest()));
- }
-
- if (mechanism.Equals("NONEwithECDSA"))
- {
- return (new DsaDigestSigner(new ECDsaSigner(), new NullDigest()));
- }
- if (mechanism.Equals("SHA-1withECDSA"))
+ if (mechanism.Equals("SHA-224withDSA"))
+ {
+ return (new DsaDigestSigner(new DsaSigner(), new Sha224Digest()));
+ }
+ if (mechanism.Equals("SHA-256withDSA"))
+ {
+ return (new DsaDigestSigner(new DsaSigner(), new Sha256Digest()));
+ }
+ if (mechanism.Equals("SHA-384withDSA"))
+ {
+ return (new DsaDigestSigner(new DsaSigner(), new Sha384Digest()));
+ }
+ if (mechanism.Equals("SHA-512withDSA"))
+ {
+ return (new DsaDigestSigner(new DsaSigner(), new Sha512Digest()));
+ }
+
+ if (mechanism.Equals("NONEwithECDSA"))
+ {
+ return (new DsaDigestSigner(new ECDsaSigner(), new NullDigest()));
+ }
+ if (mechanism.Equals("SHA-1withECDSA"))
{
return (new DsaDigestSigner(new ECDsaSigner(), new Sha1Digest()));
}
- if (mechanism.Equals("SHA-224withECDSA"))
- {
- return (new DsaDigestSigner(new ECDsaSigner(), new Sha224Digest()));
- }
- if (mechanism.Equals("SHA-256withECDSA"))
- {
- return (new DsaDigestSigner(new ECDsaSigner(), new Sha256Digest()));
- }
- if (mechanism.Equals("SHA-384withECDSA"))
- {
- return (new DsaDigestSigner(new ECDsaSigner(), new Sha384Digest()));
- }
- if (mechanism.Equals("SHA-512withECDSA"))
- {
- return (new DsaDigestSigner(new ECDsaSigner(), new Sha512Digest()));
- }
-
- if (mechanism.Equals("RIPEMD160withECDSA"))
- {
- return (new DsaDigestSigner(new ECDsaSigner(), new RipeMD160Digest()));
- }
-
- if (mechanism.Equals("SHA1WITHECNR"))
- {
- return (new DsaDigestSigner(new ECNRSigner(), new Sha1Digest()));
- }
- if (mechanism.Equals("SHA224WITHECNR"))
- {
- return (new DsaDigestSigner(new ECNRSigner(), new Sha224Digest()));
- }
- if (mechanism.Equals("SHA256WITHECNR"))
- {
- return (new DsaDigestSigner(new ECNRSigner(), new Sha256Digest()));
- }
- if (mechanism.Equals("SHA384WITHECNR"))
- {
- return (new DsaDigestSigner(new ECNRSigner(), new Sha384Digest()));
- }
- if (mechanism.Equals("SHA512WITHECNR"))
- {
- return (new DsaDigestSigner(new ECNRSigner(), new Sha512Digest()));
- }
-
- if (mechanism.Equals("GOST3410"))
- {
- return new Gost3410DigestSigner(new Gost3410Signer(), new Gost3411Digest());
- }
- if (mechanism.Equals("ECGOST3410"))
- {
- return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411Digest());
- }
-
- if (mechanism.Equals("SHA1WITHRSA/ISO9796-2"))
- {
- return new Iso9796d2Signer(new RsaBlindedEngine(), new Sha1Digest(), true);
- }
- if (mechanism.Equals("MD5WITHRSA/ISO9796-2"))
- {
- return new Iso9796d2Signer(new RsaBlindedEngine(), new MD5Digest(), true);
- }
- if (mechanism.Equals("RIPEMD160WITHRSA/ISO9796-2"))
- {
- return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true);
- }
-
- throw new SecurityUtilityException("Signer " + algorithm + " not recognised.");
+ if (mechanism.Equals("SHA-224withECDSA"))
+ {
+ return (new DsaDigestSigner(new ECDsaSigner(), new Sha224Digest()));
+ }
+ if (mechanism.Equals("SHA-256withECDSA"))
+ {
+ return (new DsaDigestSigner(new ECDsaSigner(), new Sha256Digest()));
+ }
+ if (mechanism.Equals("SHA-384withECDSA"))
+ {
+ return (new DsaDigestSigner(new ECDsaSigner(), new Sha384Digest()));
+ }
+ if (mechanism.Equals("SHA-512withECDSA"))
+ {
+ return (new DsaDigestSigner(new ECDsaSigner(), new Sha512Digest()));
+ }
+
+ if (mechanism.Equals("RIPEMD160withECDSA"))
+ {
+ return (new DsaDigestSigner(new ECDsaSigner(), new RipeMD160Digest()));
+ }
+
+ if (mechanism.Equals("SHA1WITHECNR"))
+ {
+ return (new DsaDigestSigner(new ECNRSigner(), new Sha1Digest()));
+ }
+ if (mechanism.Equals("SHA224WITHECNR"))
+ {
+ return (new DsaDigestSigner(new ECNRSigner(), new Sha224Digest()));
+ }
+ if (mechanism.Equals("SHA256WITHECNR"))
+ {
+ return (new DsaDigestSigner(new ECNRSigner(), new Sha256Digest()));
+ }
+ if (mechanism.Equals("SHA384WITHECNR"))
+ {
+ return (new DsaDigestSigner(new ECNRSigner(), new Sha384Digest()));
+ }
+ if (mechanism.Equals("SHA512WITHECNR"))
+ {
+ return (new DsaDigestSigner(new ECNRSigner(), new Sha512Digest()));
+ }
+
+ if (mechanism.Equals("GOST3410"))
+ {
+ return new Gost3410DigestSigner(new Gost3410Signer(), new Gost3411Digest());
+ }
+ if (mechanism.Equals("ECGOST3410"))
+ {
+ return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411Digest());
+ }
+
+ if (mechanism.Equals("SHA1WITHRSA/ISO9796-2"))
+ {
+ return new Iso9796d2Signer(new RsaBlindedEngine(), new Sha1Digest(), true);
+ }
+ if (mechanism.Equals("MD5WITHRSA/ISO9796-2"))
+ {
+ return new Iso9796d2Signer(new RsaBlindedEngine(), new MD5Digest(), true);
+ }
+ if (mechanism.Equals("RIPEMD160WITHRSA/ISO9796-2"))
+ {
+ return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true);
+ }
+
+ throw new SecurityUtilityException("Signer " + algorithm + " not recognised.");
}
public static string GetEncodingName(
- DerObjectIdentifier oid)
+ DerObjectIdentifier oid)
{
return (string) algorithms[oid.Id];
}
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index e629fcf65..27fd18d6d 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -1,6 +1,8 @@
using System;
using System.Text;
+using Org.BouncyCastle.Math;
+
namespace Org.BouncyCastle.Utilities
{
/// <summary> General array utilities.</summary>
@@ -407,11 +409,90 @@ namespace Org.BouncyCastle.Utilities
}
}
- public static byte[] Copy(byte[] data, int off, int len)
+ public static byte[] CopyOf(byte[] data, int newLength)
{
- byte[] result = new byte[len];
- Array.Copy(data, off, result, 0, len);
- return result;
+ byte[] tmp = new byte[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static char[] CopyOf(char[] data, int newLength)
+ {
+ char[] tmp = new char[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static int[] CopyOf(int[] data, int newLength)
+ {
+ int[] tmp = new int[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static long[] CopyOf(long[] data, int newLength)
+ {
+ long[] tmp = new long[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static BigInteger[] CopyOf(BigInteger[] data, int newLength)
+ {
+ BigInteger[] tmp = new BigInteger[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ /**
+ * Make a copy of a range of bytes from the passed in data array. The range can
+ * extend beyond the end of the input array, in which case the return array will
+ * be padded with zeroes.
+ *
+ * @param data the array from which the data is to be copied.
+ * @param from the start index at which the copying should take place.
+ * @param to the final index of the range (exclusive).
+ *
+ * @return a new byte array containing the range given.
+ */
+ public static byte[] CopyOfRange(byte[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ byte[] tmp = new byte[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ public static int[] CopyOfRange(int[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ int[] tmp = new int[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ public static long[] CopyOfRange(long[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ long[] tmp = new long[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ public static BigInteger[] CopyOfRange(BigInteger[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ BigInteger[] tmp = new BigInteger[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ private static int GetLength(int from, int to)
+ {
+ int newLength = to - from;
+ if (newLength < 0)
+ throw new ArgumentException(from + " > " + to);
+ return newLength;
}
public static byte[] Append(byte[] a, byte b)
@@ -463,6 +544,19 @@ namespace Org.BouncyCastle.Utilities
return rv;
}
+ public static int[] Concatenate(int[] a, int[] b)
+ {
+ if (a == null)
+ return Clone(b);
+ if (b == null)
+ return Clone(a);
+
+ int[] rv = new int[a.Length + b.Length];
+ Array.Copy(a, 0, rv, 0, a.Length);
+ Array.Copy(b, 0, rv, a.Length, b.Length);
+ return rv;
+ }
+
public static byte[] Prepend(byte[] a, byte b)
{
if (a == null)
diff --git a/crypto/src/util/Times.cs b/crypto/src/util/Times.cs
new file mode 100644
index 000000000..99a78d21a
--- /dev/null
+++ b/crypto/src/util/Times.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities
+{
+ public sealed class Times
+ {
+ private static long NanosecondsPerTick = 100L;
+
+ public static long NanoTime()
+ {
+ return DateTime.UtcNow.Ticks * NanosecondsPerTick;
+ }
+ }
+}
|