summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/asn1/Asn1Set.cs100
-rw-r--r--crypto/src/asn1/pkcs/PrivateKeyInfo.cs2
-rw-r--r--crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs145
-rw-r--r--crypto/src/crypto/agreement/DHStandardGroups.cs206
-rw-r--r--crypto/src/crypto/engines/IesEngine.cs2
-rw-r--r--crypto/src/crypto/macs/Poly1305.cs543
-rw-r--r--crypto/src/crypto/signers/DsaSigner.cs45
-rw-r--r--crypto/src/crypto/signers/ECDsaSigner.cs43
-rw-r--r--crypto/src/crypto/signers/GenericSigner.cs229
-rw-r--r--crypto/src/crypto/signers/HMacDsaKCalculator.cs150
-rw-r--r--crypto/src/crypto/signers/IDsaKCalculator.cs44
-rw-r--r--crypto/src/crypto/signers/RandomDsaKCalculator.cs44
-rw-r--r--crypto/src/crypto/signers/RsaDigestSigner.cs192
-rw-r--r--crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCipherFactory.cs15
-rw-r--r--crypto/src/crypto/tls/AbstractTlsClient.cs232
-rw-r--r--crypto/src/crypto/tls/AbstractTlsContext.cs132
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCredentials.cs10
-rw-r--r--crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsKeyExchange.cs166
-rw-r--r--crypto/src/crypto/tls/AbstractTlsPeer.cs48
-rw-r--r--crypto/src/crypto/tls/AbstractTlsServer.cs321
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSigner.cs50
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs20
-rw-r--r--crypto/src/crypto/tls/AlertDescription.cs74
-rw-r--r--crypto/src/crypto/tls/AlertLevel.cs18
-rw-r--r--crypto/src/crypto/tls/AlwaysValidVerifyer.cs24
-rw-r--r--crypto/src/crypto/tls/Certificate.cs7
-rw-r--r--crypto/src/crypto/tls/CertificateRequest.cs14
-rw-r--r--crypto/src/crypto/tls/Chacha20Poly1305.cs153
-rw-r--r--crypto/src/crypto/tls/CombinedHash.cs203
-rw-r--r--crypto/src/crypto/tls/CompressionMethod.cs2
-rw-r--r--crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs71
-rw-r--r--crypto/src/crypto/tls/DefaultTlsCipherFactory.cs204
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs620
-rw-r--r--crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs51
-rw-r--r--crypto/src/crypto/tls/DefaultTlsServer.cs548
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs94
-rw-r--r--crypto/src/crypto/tls/DeferredHash.cs201
-rw-r--r--crypto/src/crypto/tls/DigestAlgorithm.cs24
-rw-r--r--crypto/src/crypto/tls/DigitallySigned.cs70
-rw-r--r--crypto/src/crypto/tls/ExporterLabel.cs11
-rw-r--r--crypto/src/crypto/tls/ExtensionType.cs18
-rw-r--r--crypto/src/crypto/tls/FiniteFieldDheGroup.cs21
-rw-r--r--crypto/src/crypto/tls/HeartbeatExtension.cs52
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessage.cs102
-rw-r--r--crypto/src/crypto/tls/ICertificateVerifyer.cs18
-rw-r--r--crypto/src/crypto/tls/LegacyTlsAuthentication.cs30
-rw-r--r--crypto/src/crypto/tls/LegacyTlsClient.cs26
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs379
-rw-r--r--crypto/src/crypto/tls/RecordStream.cs355
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs107
-rw-r--r--crypto/src/crypto/tls/ServerDHParams.cs60
-rw-r--r--crypto/src/crypto/tls/ServerName.cs105
-rw-r--r--crypto/src/crypto/tls/ServerNameList.cs81
-rw-r--r--crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs15
-rw-r--r--crypto/src/crypto/tls/SrpTlsClient.cs218
-rw-r--r--crypto/src/crypto/tls/Ssl3Mac.cs206
-rw-r--r--crypto/src/crypto/tls/SupplementalDataEntry.cs26
-rw-r--r--crypto/src/crypto/tls/TlsAeadCipher.cs189
-rw-r--r--crypto/src/crypto/tls/TlsAgreementCredentials.cs11
-rw-r--r--crypto/src/crypto/tls/TlsBlockCipher.cs337
-rw-r--r--crypto/src/crypto/tls/TlsCipher.cs6
-rw-r--r--crypto/src/crypto/tls/TlsCipherFactory.cs3
-rw-r--r--crypto/src/crypto/tls/TlsClient.cs58
-rw-r--r--crypto/src/crypto/tls/TlsClientContext.cs12
-rw-r--r--crypto/src/crypto/tls/TlsClientContextImpl.cs41
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs885
-rw-r--r--crypto/src/crypto/tls/TlsDHKeyExchange.cs204
-rw-r--r--crypto/src/crypto/tls/TlsDHUtilities.cs419
-rw-r--r--crypto/src/crypto/tls/TlsDheKeyExchange.cs97
-rw-r--r--crypto/src/crypto/tls/TlsDsaSigner.cs76
-rw-r--r--crypto/src/crypto/tls/TlsDssSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsECDHKeyExchange.cs251
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs190
-rw-r--r--crypto/src/crypto/tls/TlsECDsaSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsEccUtilities.cs35
-rw-r--r--crypto/src/crypto/tls/TlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/TlsExtensionsUtilities.cs258
-rw-r--r--crypto/src/crypto/tls/TlsFatalAlert.cs6
-rw-r--r--crypto/src/crypto/tls/TlsHandshakeHash.cs22
-rw-r--r--crypto/src/crypto/tls/TlsKeyExchange.cs80
-rw-r--r--crypto/src/crypto/tls/TlsMac.cs201
-rw-r--r--crypto/src/crypto/tls/TlsNullCipher.cs108
-rw-r--r--crypto/src/crypto/tls/TlsPeer.cs43
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs1040
-rw-r--r--crypto/src/crypto/tls/TlsProtocolHandler.cs1176
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs232
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs147
-rw-r--r--crypto/src/crypto/tls/TlsRsaSigner.cs82
-rw-r--r--crypto/src/crypto/tls/TlsRsaUtilities.cs152
-rw-r--r--crypto/src/crypto/tls/TlsServer.cs90
-rw-r--r--crypto/src/crypto/tls/TlsServerContext.cs11
-rw-r--r--crypto/src/crypto/tls/TlsServerContextImpl.cs20
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs753
-rw-r--r--crypto/src/crypto/tls/TlsSessionImpl.cs54
-rw-r--r--crypto/src/crypto/tls/TlsSigner.cs23
-rw-r--r--crypto/src/crypto/tls/TlsSignerCredentials.cs13
-rw-r--r--crypto/src/crypto/tls/TlsSrpKeyExchange.cs186
-rw-r--r--crypto/src/crypto/tls/TlsSrpUtilities.cs41
-rw-r--r--crypto/src/crypto/tls/TlsSrtpUtilities.cs62
-rw-r--r--crypto/src/crypto/tls/TlsStream.cs119
-rw-r--r--crypto/src/crypto/tls/TlsStreamCipher.cs176
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs481
-rw-r--r--crypto/src/crypto/tls/UseSrtpData.cs56
-rw-r--r--crypto/src/openssl/PEMReader.cs2
-rw-r--r--crypto/src/pkcs/Pkcs12Store.cs465
-rw-r--r--crypto/src/security/PrivateKeyFactory.cs3
-rw-r--r--crypto/src/security/SignerUtilities.cs725
-rw-r--r--crypto/src/util/Arrays.cs102
-rw-r--r--crypto/src/util/Times.cs14
111 files changed, 12046 insertions, 4752 deletions
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 &lt;= 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; + } + } +}