diff options
author | Oren Novotny <oren@novotny.org> | 2014-10-14 09:24:13 -0400 |
---|---|---|
committer | Oren Novotny <oren@novotny.org> | 2014-10-14 09:24:13 -0400 |
commit | cdd270cb0033455205cdb9a82e9632a733ef472b (patch) | |
tree | c1dba7a8dde3980e4e2db366ae611d99a5bb7608 /crypto/src | |
parent | Rename csproj to avoid conflicts (diff) | |
parent | Update ignore file (diff) | |
download | BouncyCastle.NET-ed25519-cdd270cb0033455205cdb9a82e9632a733ef472b.tar.xz |
Merge master-v12 into pcl. Includes 1.8-beta4
Diffstat (limited to 'crypto/src')
25 files changed, 1225 insertions, 761 deletions
diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs index 2e77ca2a9..31a061b40 100644 --- a/crypto/src/asn1/Asn1Set.cs +++ b/crypto/src/asn1/Asn1Set.cs @@ -1,6 +1,8 @@ using System; using System.Collections; +using System.Collections.Generic; using System.IO; +using System.Linq; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; @@ -278,67 +280,40 @@ namespace Org.BouncyCastle.Asn1 return encObj; } - /** - * return true if a <= b (arrays are assumed padded with zeros). - */ - private bool LessThanOrEqual( - byte[] a, - byte[] b) - { - int len = System.Math.Min(a.Length, b.Length); - for (int i = 0; i != len; ++i) - { - if (a[i] != b[i]) - { - return a[i] < b[i]; - } - } - return len == a.Length; - } - protected internal void Sort() { - if (_set.Count > 1) - { - bool swapped = true; - int lastSwap = _set.Count - 1; + if (_set.Count < 2) + return; - 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; - } + //Asn1Encodable[] items = new Asn1Encodable[_set.Count]; + //byte[][] keys = new byte[_set.Count][]; + + //for (int i = 0; i < _set.Count; ++i) + //{ + // Asn1Encodable item = (Asn1Encodable)_set[i]; + // items[i] = item; + // keys[i] = item.GetEncoded(Asn1Encodable.Der); + //} + + //Array.Sort(keys, items, new DerComparer()); + + var sorted = _set.Cast<Asn1Encodable>() + .Select(a => new {Item = a, Key = a.GetEncoded(Asn1Encodable.Der)}) + .OrderBy(t => t.Key, new DerComparer()) + .Select(t => t.Item) + .ToList(); + + + //List<Asn1Encodable[]> t; + + + for (int i = 0; i < _set.Count; ++i) + { + _set[i] = sorted[i]; } } - protected internal void AddObject( - Asn1Encodable obj) + protected internal void AddObject(Asn1Encodable obj) { _set.Add(obj); } @@ -347,5 +322,36 @@ namespace Org.BouncyCastle.Asn1 { return CollectionUtilities.ToString(_set); } + + private class DerComparer + : IComparer<byte[]> + { + public int Compare(byte[] x, byte[] y) + { + byte[] a = x, b = 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/crypto/agreement/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs index 6c46b60de..93b65af98 100644 --- a/crypto/src/crypto/agreement/DHStandardGroups.cs +++ b/crypto/src/crypto/agreement/DHStandardGroups.cs @@ -9,19 +9,19 @@ 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) { - BigInteger p = new BigInteger(1, Hex.Decode(hexP)); - BigInteger g = new BigInteger(1, Hex.Decode(hexG)); - return new DHParameters(p, g); + return new DHParameters(FromHex(hexP), FromHex(hexG)); } private static DHParameters FromPGQ(string hexP, string hexG, string hexQ) { - BigInteger p = new BigInteger(1, Hex.Decode(hexP)); - BigInteger g = new BigInteger(1, Hex.Decode(hexG)); - BigInteger q = new BigInteger(1, Hex.Decode(hexQ)); - return new DHParameters(p, g, q); + return new DHParameters(FromHex(hexP), FromHex(hexG), FromHex(hexQ)); } /* diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs index d815656f6..0719e18ad 100644 --- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs +++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs @@ -5,62 +5,62 @@ using System.Security.Cryptography; namespace Org.BouncyCastle.Crypto.Prng { - /// <summary> - /// Uses Microsoft's RNGCryptoServiceProvider - /// </summary> - public class CryptoApiRandomGenerator - : IRandomGenerator - { - private readonly RandomNumberGenerator rndProv; + /// <summary> + /// Uses Microsoft's RNGCryptoServiceProvider + /// </summary> + public class CryptoApiRandomGenerator + : IRandomGenerator + { + private readonly RandomNumberGenerator rndProv; - public CryptoApiRandomGenerator() - : this(new RNGCryptoServiceProvider()) - { - } + public CryptoApiRandomGenerator() + : this(new RNGCryptoServiceProvider()) + { + } - public CryptoApiRandomGenerator(RandomNumberGenerator rng) - { - this.rndProv = rng; - } + public CryptoApiRandomGenerator(RandomNumberGenerator rng) + { + this.rndProv = rng; + } - #region IRandomGenerator Members + #region IRandomGenerator Members - public virtual void AddSeedMaterial(byte[] seed) - { - // We don't care about the seed - } + public virtual void AddSeedMaterial(byte[] seed) + { + // We don't care about the seed + } - public virtual void AddSeedMaterial(long seed) - { - // We don't care about the seed - } + public virtual void AddSeedMaterial(long seed) + { + // We don't care about the seed + } - public virtual void NextBytes(byte[] bytes) - { - rndProv.GetBytes(bytes); - } + public virtual void NextBytes(byte[] bytes) + { + rndProv.GetBytes(bytes); + } - public virtual void NextBytes(byte[] bytes, int start, int len) - { - if (start < 0) - throw new ArgumentException("Start offset cannot be negative", "start"); - if (bytes.Length < (start + len)) - throw new ArgumentException("Byte array too small for requested offset and length"); + public virtual void NextBytes(byte[] bytes, int start, int len) + { + if (start < 0) + throw new ArgumentException("Start offset cannot be negative", "start"); + if (bytes.Length < (start + len)) + throw new ArgumentException("Byte array too small for requested offset and length"); - if (bytes.Length == len && start == 0) - { - NextBytes(bytes); - } - else - { - byte[] tmpBuf = new byte[len]; - rndProv.GetBytes(tmpBuf); - Array.Copy(tmpBuf, 0, bytes, start, len); - } - } + if (bytes.Length == len && start == 0) + { + NextBytes(bytes); + } + else + { + byte[] tmpBuf = new byte[len]; + NextBytes(tmpBuf); + Array.Copy(tmpBuf, 0, bytes, start, len); + } + } - #endregion - } + #endregion + } } #endif 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/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs index 47542c796..c2c6fd57c 100644 --- a/crypto/src/crypto/tls/AbstractTlsServer.cs +++ b/crypto/src/crypto/tls/AbstractTlsServer.cs @@ -223,9 +223,10 @@ namespace Org.BouncyCastle.Crypto.Tls if (this.mEncryptThenMacOffered && AllowEncryptThenMac) { /* - * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC - * request extension from a client and then selects a stream or AEAD cipher suite, it - * MUST NOT send an encrypt-then-MAC response extension back to the client. + * 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)) { @@ -233,9 +234,11 @@ namespace Org.BouncyCastle.Crypto.Tls } } - if (this.mMaxFragmentLengthOffered >= 0) + if (this.mMaxFragmentLengthOffered >= 0 + && TlsUtilities.IsValidUint8(mMaxFragmentLengthOffered) + && MaxFragmentLength.IsValid((byte)mMaxFragmentLengthOffered)) { - TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)this.mMaxFragmentLengthOffered); + TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)mMaxFragmentLengthOffered); } if (this.mTruncatedHMacOffered && AllowTruncatedHMac) 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/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs index f301ea3c0..280321e2a 100644 --- a/crypto/src/crypto/tls/ExporterLabel.cs +++ b/crypto/src/crypto/tls/ExporterLabel.cs @@ -28,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/LegacyTlsAuthentication.cs b/crypto/src/crypto/tls/LegacyTlsAuthentication.cs index 395f94208..5da9576af 100644 --- a/crypto/src/crypto/tls/LegacyTlsAuthentication.cs +++ b/crypto/src/crypto/tls/LegacyTlsAuthentication.cs @@ -1,3 +1,4 @@ +#if FALSE using System; namespace Org.BouncyCastle.Crypto.Tls @@ -28,3 +29,4 @@ namespace Org.BouncyCastle.Crypto.Tls } } } +#endif \ No newline at end of file diff --git a/crypto/src/crypto/tls/LegacyTlsClient.cs b/crypto/src/crypto/tls/LegacyTlsClient.cs index fbb9a732e..98a8bcbfc 100644 --- a/crypto/src/crypto/tls/LegacyTlsClient.cs +++ b/crypto/src/crypto/tls/LegacyTlsClient.cs @@ -1,3 +1,4 @@ +#if FALSE using System; namespace Org.BouncyCastle.Crypto.Tls @@ -23,4 +24,5 @@ namespace Org.BouncyCastle.Crypto.Tls return new LegacyTlsAuthentication(verifyer); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs index 6a115a911..12bb59f22 100644 --- a/crypto/src/crypto/tls/SecurityParameters.cs +++ b/crypto/src/crypto/tls/SecurityParameters.cs @@ -14,21 +14,13 @@ namespace Org.BouncyCastle.Crypto.Tls 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 void CopySessionParametersFrom(SecurityParameters other) - { - this.entity = other.entity; - this.cipherSuite = other.cipherSuite; - this.compressionAlgorithm = other.compressionAlgorithm; - this.prfAlgorithm = other.prfAlgorithm; - this.verifyDataLength = other.verifyDataLength; - this.masterSecret = Arrays.Clone(other.masterSecret); - } + internal bool extendedMasterSecret = false; internal virtual void Clear() { @@ -90,5 +82,10 @@ namespace Org.BouncyCastle.Crypto.Tls { get { return serverRandom; } } + + public virtual byte[] SessionHash + { + get { return sessionHash; } + } } } diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs index e48c92d30..9fe50add8 100644 --- a/crypto/src/crypto/tls/TlsClientProtocol.cs +++ b/crypto/src/crypto/tls/TlsClientProtocol.cs @@ -360,11 +360,12 @@ namespace Org.BouncyCastle.Crypto.Tls 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()); - TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish(); - if (clientCreds != null && clientCreds is TlsSignerCredentials) { TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds; @@ -386,7 +387,7 @@ namespace Org.BouncyCastle.Crypto.Tls else { signatureAndHashAlgorithm = null; - hash = GetCurrentPrfHash(Context, prepareFinishHash, null); + hash = mSecurityParameters.SessionHash; } byte[] signature = signerCredentials.GenerateCertificateSignature(hash); @@ -562,7 +563,7 @@ namespace Org.BouncyCastle.Crypto.Tls { NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf); - TlsProtocol.AssertEmpty(buf); + AssertEmpty(buf); mTlsClient.NotifyNewSessionTicket(newSessionTicket); } @@ -635,6 +636,15 @@ namespace Org.BouncyCastle.Crypto.Tls 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. * @@ -656,6 +666,25 @@ namespace Org.BouncyCastle.Crypto.Tls 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[.] @@ -667,16 +696,6 @@ namespace Org.BouncyCastle.Crypto.Tls // 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(this.mClientExtensions, extType)) - throw new TlsFatalAlert(AlertDescription.unsupported_extension); } } @@ -718,6 +737,8 @@ namespace Org.BouncyCastle.Crypto.Tls sessionClientExtensions = null; sessionServerExtensions = this.mSessionParameters.ReadServerExtensions(); + + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); } this.mSecurityParameters.cipherSuite = selectedCipherSuite; @@ -726,9 +747,10 @@ namespace Org.BouncyCastle.Crypto.Tls if (sessionServerExtensions != null) { /* - * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC - * request extension from a client and then selects a stream or AEAD cipher suite, it - * MUST NOT Send an encrypt-then-MAC response extension back to the client. + * 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)) @@ -808,6 +830,8 @@ namespace Org.BouncyCastle.Crypto.Tls this.mClientExtensions = this.mTlsClient.GetClientExtensions(); + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); TlsUtilities.WriteVersion(client_version, message); diff --git a/crypto/src/crypto/tls/TlsDHUtilities.cs b/crypto/src/crypto/tls/TlsDHUtilities.cs index 477c3ebac..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,14 +8,386 @@ 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 One = BigInteger.One; 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); @@ -78,7 +451,7 @@ namespace Org.BouncyCastle.Crypto.Tls { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - if (Y.CompareTo(Two) < 0 || Y.CompareTo(p.Subtract(One)) > 0) + if (Y.CompareTo(Two) < 0 || Y.CompareTo(p.Subtract(Two)) > 0) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs index 8876911e6..46851b66c 100644 --- a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs +++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs @@ -18,6 +18,11 @@ namespace Org.BouncyCastle.Crypto.Tls 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) { @@ -83,6 +88,13 @@ namespace Org.BouncyCastle.Crypto.Tls } /// <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); @@ -99,6 +111,11 @@ namespace Org.BouncyCastle.Crypto.Tls return CreateEmptyExtensionData(); } + public static byte[] CreateExtendedMasterSecretExtension() + { + return CreateEmptyExtensionData(); + } + /// <exception cref="IOException"></exception> public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension) { @@ -115,9 +132,6 @@ namespace Org.BouncyCastle.Crypto.Tls /// <exception cref="IOException"></exception> public static byte[] CreateMaxFragmentLengthExtension(byte maxFragmentLength) { - if (!MaxFragmentLength.IsValid(maxFragmentLength)) - throw new TlsFatalAlert(AlertDescription.internal_error); - return new byte[]{ maxFragmentLength }; } @@ -173,6 +187,12 @@ namespace Org.BouncyCastle.Crypto.Tls } /// <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) @@ -196,12 +216,7 @@ namespace Org.BouncyCastle.Crypto.Tls if (extensionData.Length != 1) throw new TlsFatalAlert(AlertDescription.decode_error); - byte maxFragmentLength = extensionData[0]; - - if (!MaxFragmentLength.IsValid(maxFragmentLength)) - throw new TlsFatalAlert(AlertDescription.illegal_parameter); - - return maxFragmentLength; + return extensionData[0]; } /// <exception cref="IOException"></exception> diff --git a/crypto/src/crypto/tls/TlsFatalAlert.cs b/crypto/src/crypto/tls/TlsFatalAlert.cs index 0c7ed88d9..55d784dd9 100644 --- a/crypto/src/crypto/tls/TlsFatalAlert.cs +++ b/crypto/src/crypto/tls/TlsFatalAlert.cs @@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Tls } public TlsFatalAlert(byte alertDescription, Exception alertCause) - : base("Fatal alert: " + alertDescription, alertCause) + : base(Tls.AlertDescription.GetText(alertDescription), alertCause) { this.alertDescription = alertDescription; } diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs index 589ede802..165d6a147 100644 --- a/crypto/src/crypto/tls/TlsServerProtocol.cs +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs @@ -422,14 +422,14 @@ namespace Org.BouncyCastle.Crypto.Tls // Verify the CertificateVerify message contains a correct signature. try { - byte[] certificateVerifyHash; + byte[] hash; if (TlsUtilities.IsTlsV12(Context)) { - certificateVerifyHash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + hash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); } else { - certificateVerifyHash = TlsProtocol.GetCurrentPrfHash(Context, mPrepareFinishHash, null); + hash = mSecurityParameters.SessionHash; } X509CertificateStructure x509Cert = mPeerCertificate.GetCertificateAt(0); @@ -439,7 +439,7 @@ namespace Org.BouncyCastle.Crypto.Tls TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType); tlsSigner.Init(Context); if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, - clientCertificateVerify.Signature, publicKey, certificateVerifyHash)) + clientCertificateVerify.Signature, publicKey, hash)) { throw new TlsFatalAlert(AlertDescription.decrypt_error); } @@ -494,6 +494,8 @@ namespace Org.BouncyCastle.Crypto.Tls */ this.mClientExtensions = ReadExtensions(buf); + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); + ContextAdmin.SetClientVersion(client_version); mTlsServer.NotifyClientVersion(client_version); @@ -556,11 +558,12 @@ namespace Org.BouncyCastle.Crypto.Tls AssertEmpty(buf); + this.mPrepareFinishHash = mRecordStream.PrepareToFinish(); + this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, mPrepareFinishHash, null); + EstablishMasterSecret(Context, mKeyExchange); mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); - this.mPrepareFinishHash = mRecordStream.PrepareToFinish(); - if (!mExpectSessionTicket) { SendChangeCipherSpecMessage(); @@ -669,6 +672,12 @@ namespace Org.BouncyCastle.Crypto.Tls } } + 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 diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index f1ea0996d..d571e5900 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -740,9 +740,7 @@ namespace Org.BouncyCastle.Crypto.Tls 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]; @@ -817,9 +815,7 @@ 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); - } } } } @@ -831,9 +827,7 @@ namespace Org.BouncyCastle.Crypto.Tls 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); } @@ -870,14 +864,19 @@ namespace Org.BouncyCastle.Crypto.Tls internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_master_secret) { SecurityParameters securityParameters = context.SecurityParameters; - byte[] seed = Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); + + byte[] seed = securityParameters.extendedMasterSecret + ? securityParameters.SessionHash + : Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); if (IsSsl(context)) - { return CalculateMasterSecret_Ssl(pre_master_secret, seed); - } - return PRF(context, pre_master_secret, ExporterLabel.master_secret, seed, 48); + 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) @@ -912,9 +911,7 @@ namespace Org.BouncyCastle.Crypto.Tls internal static byte[] CalculateVerifyData(TlsContext context, string asciiLabel, byte[] handshakeHash) { if (IsSsl(context)) - { return handshakeHash; - } SecurityParameters securityParameters = context.SecurityParameters; byte[] master_secret = securityParameters.MasterSecret; diff --git a/crypto/src/math/ec/multiplier/FpNafMultiplier.cs b/crypto/src/math/ec/multiplier/FpNafMultiplier.cs index f5a98501a..7c034ce6e 100644 --- a/crypto/src/math/ec/multiplier/FpNafMultiplier.cs +++ b/crypto/src/math/ec/multiplier/FpNafMultiplier.cs @@ -1,3 +1,4 @@ +#if FALSE namespace Org.BouncyCastle.Math.EC.Multiplier { /** @@ -37,3 +38,4 @@ namespace Org.BouncyCastle.Math.EC.Multiplier } } } +#endif \ No newline at end of file diff --git a/crypto/src/math/ec/multiplier/WNafMultiplier.cs b/crypto/src/math/ec/multiplier/WNafMultiplier.cs index b5cf34ba8..484bb30e7 100644 --- a/crypto/src/math/ec/multiplier/WNafMultiplier.cs +++ b/crypto/src/math/ec/multiplier/WNafMultiplier.cs @@ -1,3 +1,4 @@ +#if FALSE using System; namespace Org.BouncyCastle.Math.EC.Multiplier @@ -239,3 +240,4 @@ namespace Org.BouncyCastle.Math.EC.Multiplier } } } +#endif \ No newline at end of file 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 7e9976c9f..a024bcd6b 100644 --- a/crypto/src/pkcs/Pkcs12Store.cs +++ b/crypto/src/pkcs/Pkcs12Store.cs @@ -29,6 +29,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; @@ -108,95 +110,22 @@ namespace Org.BouncyCastle.Pkcs Load(input, password); } - public void Load( - Stream input, - char[] password) + protected virtual void LoadKeyBag(PrivateKeyInfo privKeyInfo, Asn1Set bagAttributes) { - 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 - { - MacData mData = bag.MacData; - DigestInfo dInfo = mData.Mac; - AlgorithmIdentifier algId = dInfo.AlgorithmID; - byte[] salt = mData.GetSalt(); - int itCount = mData.IterationCount.IntValue; - - byte[] data = ((Asn1OctetString) info.Content).GetOctets(); - - byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data); - byte[] dig = dInfo.GetDigest(); - - if (!Arrays.ConstantTimeAreEqual(mac, dig)) - { - if (password.Length > 0) - throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); - - // Try with incorrect zero length password - mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data); - - if (!Arrays.ConstantTimeAreEqual(mac, dig)) - throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); - - wrongPkcs12Zero = true; - } - } - - keys.Clear(); - localIds.Clear(); - - IList chain = Platform.CreateArrayList(); - - if (info.ContentType.Equals(PkcsObjectIdentifiers.Data)) - { - byte[] octs = ((Asn1OctetString)info.Content).GetOctets(); - AuthenticatedSafe authSafe = new AuthenticatedSafe( - (Asn1Sequence) Asn1OctetString.FromByteArray(octs)); - ContentInfo[] cis = authSafe.GetContentInfo(); - - foreach (ContentInfo ci in cis) - { - DerObjectIdentifier oid = ci.ContentType; - - if (oid.Equals(PkcsObjectIdentifiers.Data)) - { - byte[] octets = ((Asn1OctetString)ci.Content).GetOctets(); - Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo); - foreach (Asn1Sequence subSeq in seq) - { - 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); + AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privKey, attributes); + string alias = null; Asn1OctetString localId = null; - if (b.BagAttributes != null) + if (bagAttributes != null) { - foreach (Asn1Sequence sq in b.BagAttributes) + foreach (Asn1Sequence sq in bagAttributes) { - DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; - Asn1Set attrSet = (Asn1Set) sq[1]; + DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]); + Asn1Set attrSet = Asn1Set.GetInstance(sq[1]); Asn1Encodable attr = null; if (attrSet.Count > 0) @@ -210,10 +139,8 @@ namespace Org.BouncyCastle.Pkcs { // 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); @@ -223,7 +150,7 @@ namespace Org.BouncyCastle.Pkcs { alias = ((DerBmpString)attr).GetString(); // TODO Do these in a separate loop, just collect aliases here - keys[alias] = pkcs12Key; + keys[alias] = keyEntry; } else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) { @@ -239,7 +166,7 @@ namespace Org.BouncyCastle.Pkcs if (alias == null) { - keys[name] = pkcs12Key; + keys[name] = keyEntry; } else { @@ -249,183 +176,124 @@ namespace Org.BouncyCastle.Pkcs } else { - unmarkedKey = true; - keys["unmarked"] = pkcs12Key; + unmarkedKeyEntry = keyEntry; } } - else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) + + protected virtual void LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo encPrivKeyInfo, Asn1Set bagAttributes, + char[] password, bool wrongPkcs12Zero) { - chain.Add(b); - } - else + if (password != null) { - Debug.WriteLine("extra " + b.BagID); - Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo( + password, wrongPkcs12Zero, encPrivKeyInfo); + + LoadKeyBag(privInfo, bagAttributes); } } - } - else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) - { - EncryptedData d = EncryptedData.GetInstance(ci.Content); - byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm, - password, wrongPkcs12Zero, d.Content.GetOctets()); - Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); - foreach (Asn1Sequence subSeq in seq) + public void Load( + Stream input, + char[] password) { - SafeBag b = new SafeBag(subSeq); + if (input == null) + throw new ArgumentNullException("input"); - if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) - { - chain.Add(b); - } - else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag)) + Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input); + Pfx bag = new Pfx(obj); + ContentInfo info = bag.AuthSafe; + bool wrongPkcs12Zero = false; + + if (password != null && bag.MacData != null) // check the mac code { - 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; + MacData mData = bag.MacData; + DigestInfo dInfo = mData.Mac; + AlgorithmIdentifier algId = dInfo.AlgorithmID; + byte[] salt = mData.GetSalt(); + int itCount = mData.IterationCount.IntValue; - foreach (Asn1Sequence sq in b.BagAttributes) - { - DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; - Asn1Set attrSet = (Asn1Set) sq[1]; - Asn1Encodable attr = null; + byte[] data = ((Asn1OctetString) info.Content).GetOctets(); + + byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data); + byte[] dig = dInfo.GetDigest(); - if (attrSet.Count > 0) + if (!Arrays.ConstantTimeAreEqual(mac, dig)) { - // TODO We should be adding all attributes in the set - attr = attrSet[0]; + if (password.Length > 0) + throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); - // 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); - } + // Try with incorrect zero length password + mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data); - if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) - { - alias = ((DerBmpString)attr).GetString(); - // TODO Do these in a separate loop, just collect aliases here - keys[alias] = pkcs12Key; + if (!Arrays.ConstantTimeAreEqual(mac, dig)) + throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); + + wrongPkcs12Zero = true; } - else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) - { - localId = (Asn1OctetString)attr; } - } - } - // TODO Should we be checking localIds != null here - // as for PkcsObjectIdentifiers.Data version above? + keys.Clear(); + localIds.Clear(); + unmarkedKeyEntry = null; - string name = Hex.ToHexString(localId.GetOctets()); + IList certBags = Platform.CreateArrayList(); - if (alias == null) - { - keys[name] = pkcs12Key; - } - else - { - // TODO There may have been more than one alias - localIds[alias] = name; - } - } - else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag)) + if (info.ContentType.Equals(PkcsObjectIdentifiers.Data)) { - 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); + byte[] octs = ((Asn1OctetString)info.Content).GetOctets(); + AuthenticatedSafe authSafe = new AuthenticatedSafe( + (Asn1Sequence) Asn1OctetString.FromByteArray(octs)); + ContentInfo[] cis = authSafe.GetContentInfo(); - foreach (Asn1Sequence sq in b.BagAttributes) + foreach (ContentInfo ci in cis) { - DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]); - Asn1Set attrSet = Asn1Set.GetInstance(sq[1]); - Asn1Encodable attr = null; + DerObjectIdentifier oid = ci.ContentType; - if (attrSet.Count > 0) + byte[] octets = null; + if (oid.Equals(PkcsObjectIdentifiers.Data)) { - // 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)) + octets = ((Asn1OctetString)ci.Content).GetOctets(); + } + else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) { - // OK, but the value has to be the same - if (!attributes[aOid.Id].Equals(attr)) + if (password != null) { - throw new IOException("attempt to add existing attribute with different value"); + EncryptedData d = EncryptedData.GetInstance(ci.Content); + octets = CryptPbeData(false, d.EncryptionAlgorithm, + password, wrongPkcs12Zero, d.Content.GetOctets()); } } else { - attributes.Add(aOid.Id, attr); + // TODO Other data types } - if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + if (octets != null) { - 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? + Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(octets); - string name = Hex.ToHexString(localId.GetOctets()); + foreach (Asn1Sequence subSeq in seq) + { + SafeBag b = new SafeBag(subSeq); - if (alias == null) + if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) { - keys[name] = pkcs12Key; + certBags.Add(b); } - else + else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag)) { - // 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)) + { + LoadKeyBag(PrivateKeyInfo.GetInstance(b.BagValue), b.BagAttributes); } else { - Debug.WriteLine("extra " + b.BagID); - Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b)); - } + // TODO Other bag types } } - else - { - Debug.WriteLine("extra " + oid); - Debug.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content)); } } } @@ -434,10 +302,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); // @@ -487,21 +355,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 @@ -510,13 +375,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; } } } @@ -842,24 +707,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( + 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(); @@ -904,13 +779,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 @@ -919,7 +792,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(); @@ -973,10 +846,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); } @@ -1027,10 +897,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); } @@ -1063,22 +930,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(); + ContentInfo certsInfo; + if (password == null) + { + 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[] - { - new ContentInfo(PkcsObjectIdentifiers.Data, keyString), - new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object()) - }; + ContentInfo[] info = new ContentInfo[]{ keysInfo, certsInfo }; byte[] data = new AuthenticatedSafe(info).GetEncoded( useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber); @@ -1088,6 +957,9 @@ namespace Org.BouncyCastle.Pkcs // // create the mac // + MacData macData = null; + if (password != null) + { byte[] mSalt = new byte[20]; random.NextBytes(mSalt); @@ -1098,12 +970,13 @@ namespace Org.BouncyCastle.Pkcs 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/SecureRandom.cs b/crypto/src/security/SecureRandom.cs index ac9d98158..dc91069a1 100644 --- a/crypto/src/security/SecureRandom.cs +++ b/crypto/src/security/SecureRandom.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; @@ -8,221 +9,247 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Security { public class SecureRandom - : Random + : Random { - // Note: all objects of this class should be deriving their random data from - // a single generator appropriate to the digest being used. - private static readonly IRandomGenerator sha1Generator = new DigestRandomGenerator(new Sha1Digest()); - private static readonly IRandomGenerator sha256Generator = new DigestRandomGenerator(new Sha256Digest()); - - private static readonly SecureRandom[] master = { null }; - private static SecureRandom Master - { - get - { - if (master[0] == null) - { - IRandomGenerator gen = sha256Generator; - gen = new ReversedWindowGenerator(gen, 32); - SecureRandom sr = master[0] = new SecureRandom(gen); - - sr.SetSeed(DateTime.Now.Ticks); - sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(24, true)); - sr.GenerateSeed(1 + sr.Next(32)); - } - - return master[0]; - } - } - - public static SecureRandom GetInstance( - string algorithm) - { - // TODO Compared to JDK, we don't auto-seed if the client forgets - problem? - - // TODO Support all digests more generally, by stripping PRNG and calling DigestUtilities? - string drgName = Platform.ToUpperInvariant(algorithm); - - IRandomGenerator drg = null; - if (drgName == "SHA1PRNG") - { - drg = sha1Generator; - } - else if (drgName == "SHA256PRNG") - { - drg = sha256Generator; - } - - if (drg != null) - { - return new SecureRandom(drg); - } - - throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); - } - - public static byte[] GetSeed( - int length) - { - return Master.GenerateSeed(length); - } - - protected IRandomGenerator generator; - - public SecureRandom() - : this(sha1Generator) - { - SetSeed(GetSeed(8)); - } - - public SecureRandom( - byte[] inSeed) - : this(sha1Generator) - { - SetSeed(inSeed); - } - - /// <summary>Use the specified instance of IRandomGenerator as random source.</summary> - /// <remarks> - /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the - /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide - /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c> - /// implementation. - /// </remarks> - /// <param name="generator">The source to generate all random bytes from.</param> - public SecureRandom( - IRandomGenerator generator) - : base(0) - { - this.generator = generator; - } - - public virtual byte[] GenerateSeed( - int length) - { - SetSeed(DateTime.Now.Ticks); - - byte[] rv = new byte[length]; - NextBytes(rv); - return rv; - } - - public virtual void SetSeed( - byte[] inSeed) - { - generator.AddSeedMaterial(inSeed); - } - - public virtual void SetSeed( - long seed) - { - generator.AddSeedMaterial(seed); - } - - public override int Next() - { - for (;;) - { - int i = NextInt() & int.MaxValue; - - if (i != int.MaxValue) - return i; - } - } - - public override int Next( - int maxValue) - { - if (maxValue < 2) - { - if (maxValue < 0) - throw new ArgumentOutOfRangeException("maxValue", "cannot be negative"); - - return 0; - } - - // Test whether maxValue is a power of 2 - if ((maxValue & -maxValue) == maxValue) - { - int val = NextInt() & int.MaxValue; - long lr = ((long) maxValue * (long) val) >> 31; - return (int) lr; - } - - int bits, result; - do - { - bits = NextInt() & int.MaxValue; - result = bits % maxValue; - } - while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow - - return result; - } - - public override int Next( - int minValue, - int maxValue) - { - if (maxValue <= minValue) - { - if (maxValue == minValue) - return minValue; - - throw new ArgumentException("maxValue cannot be less than minValue"); - } - - int diff = maxValue - minValue; - if (diff > 0) - return minValue + Next(diff); - - for (;;) - { - int i = NextInt(); - - if (i >= minValue && i < maxValue) - return i; - } - } - - public override void NextBytes( - byte[] buffer) - { - generator.NextBytes(buffer); - } - - public virtual void NextBytes( - byte[] buffer, - int start, - int length) - { - generator.NextBytes(buffer, start, length); - } - - private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0); - - public override double NextDouble() - { - return Convert.ToDouble((ulong) NextLong()) / DoubleScale; - } - - public virtual int NextInt() - { - byte[] intBytes = new byte[4]; + private static long counter = Times.NanoTime(); + + private static long NextCounterValue() + { + return Interlocked.Increment(ref counter); + } + +#if NETCF_1_0 || PCL + private static readonly SecureRandom[] master = { null }; + private static SecureRandom Master + { + get + { + lock (master) + { + if (master[0] == null) + { + SecureRandom sr = master[0] = GetInstance("SHA256PRNG", false); + + // Even though Ticks has at most 8 or 14 bits of entropy, there's no harm in adding it. + sr.SetSeed(DateTime.Now.Ticks); + + // 32 will be enough when ThreadedSeedGenerator is fixed. Until then, ThreadedSeedGenerator returns low + // entropy, and this is not sufficient to be secure. http://www.bouncycastle.org/csharpdevmailarchive/msg00814.html + sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(32, true)); + } + + return master[0]; + } + } + } +#else + private static readonly SecureRandom master = new SecureRandom(new CryptoApiRandomGenerator()); + private static SecureRandom Master + { + get { return master; } + } +#endif + + private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed) + { + IDigest digest = DigestUtilities.GetDigest(digestName); + if (digest == null) + return null; + DigestRandomGenerator prng = new DigestRandomGenerator(digest); + if (autoSeed) + { + prng.AddSeedMaterial(NextCounterValue()); + prng.AddSeedMaterial(GetSeed(digest.GetDigestSize())); + } + return prng; + } + + /// <summary> + /// Create and auto-seed an instance based on the given algorithm. + /// </summary> + /// <remarks>Equivalent to GetInstance(algorithm, true)</remarks> + /// <param name="algorithm">e.g. "SHA256PRNG"</param> + public static SecureRandom GetInstance(string algorithm) + { + return GetInstance(algorithm, true); + } + + /// <summary> + /// Create an instance based on the given algorithm, with optional auto-seeding + /// </summary> + /// <param name="algorithm">e.g. "SHA256PRNG"</param> + /// <param name="autoSeed">If true, the instance will be auto-seeded.</param> + public static SecureRandom GetInstance(string algorithm, bool autoSeed) + { + string upper = Platform.ToUpperInvariant(algorithm); + if (upper.EndsWith("PRNG")) + { + string digestName = upper.Substring(0, upper.Length - "PRNG".Length); + DigestRandomGenerator prng = CreatePrng(digestName, autoSeed); + if (prng != null) + { + return new SecureRandom(prng); + } + } + + throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); + } + + public static byte[] GetSeed(int length) + { +#if NETCF_1_0 + lock (master) +#endif + return Master.GenerateSeed(length); + } + + protected readonly IRandomGenerator generator; + + public SecureRandom() + : this(CreatePrng("SHA256", true)) + { + } + + /// <remarks> + /// To replicate existing predictable output, replace with GetInstance("SHA1PRNG", false), followed by SetSeed(seed) + /// </remarks> + [Obsolete("Use GetInstance/SetSeed instead")] + public SecureRandom(byte[] seed) + : this(CreatePrng("SHA1", false)) + { + SetSeed(seed); + } + + /// <summary>Use the specified instance of IRandomGenerator as random source.</summary> + /// <remarks> + /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the + /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide + /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c> + /// implementation. + /// </remarks> + /// <param name="generator">The source to generate all random bytes from.</param> + public SecureRandom(IRandomGenerator generator) + : base(0) + { + this.generator = generator; + } + + public virtual byte[] GenerateSeed(int length) + { + SetSeed(DateTime.Now.Ticks); + + byte[] rv = new byte[length]; + NextBytes(rv); + return rv; + } + + public virtual void SetSeed(byte[] seed) + { + generator.AddSeedMaterial(seed); + } + + public virtual void SetSeed(long seed) + { + generator.AddSeedMaterial(seed); + } + + public override int Next() + { + for (;;) + { + int i = NextInt() & int.MaxValue; + + if (i != int.MaxValue) + return i; + } + } + + public override int Next(int maxValue) + { + if (maxValue < 2) + { + if (maxValue < 0) + throw new ArgumentOutOfRangeException("maxValue", "cannot be negative"); + + return 0; + } + + // Test whether maxValue is a power of 2 + if ((maxValue & -maxValue) == maxValue) + { + int val = NextInt() & int.MaxValue; + long lr = ((long) maxValue * (long) val) >> 31; + return (int) lr; + } + + int bits, result; + do + { + bits = NextInt() & int.MaxValue; + result = bits % maxValue; + } + while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow + + return result; + } + + public override int Next(int minValue, int maxValue) + { + if (maxValue <= minValue) + { + if (maxValue == minValue) + return minValue; + + throw new ArgumentException("maxValue cannot be less than minValue"); + } + + int diff = maxValue - minValue; + if (diff > 0) + return minValue + Next(diff); + + for (;;) + { + int i = NextInt(); + + if (i >= minValue && i < maxValue) + return i; + } + } + + public override void NextBytes(byte[] buf) + { + generator.NextBytes(buf); + } + + public virtual void NextBytes(byte[] buf, int off, int len) + { + generator.NextBytes(buf, off, len); + } + + private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0); + + public override double NextDouble() + { + return Convert.ToDouble((ulong) NextLong()) / DoubleScale; + } + + public virtual int NextInt() + { + byte[] intBytes = new byte[4]; NextBytes(intBytes); - int result = 0; + int result = 0; for (int i = 0; i < 4; i++) { result = (result << 8) + (intBytes[i] & 0xff); } - return result; + return result; } - public virtual long NextLong() - { - return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); - } + public virtual long NextLong() + { + return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); + } } } |