diff options
Diffstat (limited to 'crypto/src')
45 files changed, 1463 insertions, 406 deletions
diff --git a/crypto/src/asn1/Asn1Object.cs b/crypto/src/asn1/Asn1Object.cs index a86fdbb4a..4faa81ac8 100644 --- a/crypto/src/asn1/Asn1Object.cs +++ b/crypto/src/asn1/Asn1Object.cs @@ -6,11 +6,13 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1Object : Asn1Encodable { - /// <summary>Create a base ASN.1 object from a byte array.</summary> - /// <param name="data">The byte array to parse.</param> - /// <returns>The base ASN.1 object represented by the byte array.</returns> - /// <exception cref="IOException">If there is a problem parsing the data.</exception> - public static Asn1Object FromByteArray( + /// <summary>Create a base ASN.1 object from a byte array.</summary> + /// <param name="data">The byte array to parse.</param> + /// <returns>The base ASN.1 object represented by the byte array.</returns> + /// <exception cref="IOException"> + /// If there is a problem parsing the data, or parsing an object did not exhaust the available data. + /// </exception> + public static Asn1Object FromByteArray( byte[] data) { try diff --git a/crypto/src/asn1/crmf/CertReqMsg.cs b/crypto/src/asn1/crmf/CertReqMsg.cs index 20fd4179a..03ce32d99 100644 --- a/crypto/src/asn1/crmf/CertReqMsg.cs +++ b/crypto/src/asn1/crmf/CertReqMsg.cs @@ -39,6 +39,13 @@ namespace Org.BouncyCastle.Asn1.Crmf return null; } + public static CertReqMsg GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + /** * Creates a new CertReqMsg. * @param certReq CertRequest diff --git a/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs b/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs index 9311a3ac1..b322ef233 100644 --- a/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs +++ b/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs @@ -27,5 +27,10 @@ namespace Org.BouncyCastle.Asn1.Gnu public static readonly DerObjectIdentifier Serpent256Cfb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB public static readonly DerObjectIdentifier Crc = new DerObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms public static readonly DerObjectIdentifier Crc32 = new DerObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32 + + /** 1.3.6.1.4.1.11591.15 - ellipticCurve */ + public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.6.1.4.1.11591.15"); + + public static readonly DerObjectIdentifier Ed25519 = EllipticCurve.Branch("1"); } } diff --git a/crypto/src/asn1/pkcs/CertificationRequest.cs b/crypto/src/asn1/pkcs/CertificationRequest.cs index 35bdd56eb..98caa2268 100644 --- a/crypto/src/asn1/pkcs/CertificationRequest.cs +++ b/crypto/src/asn1/pkcs/CertificationRequest.cs @@ -47,7 +47,8 @@ namespace Org.BouncyCastle.Asn1.Pkcs this.sigBits = signature; } - public CertificationRequest( + [Obsolete("Use 'GetInstance' instead")] + public CertificationRequest( Asn1Sequence seq) { if (seq.Count != 3) diff --git a/crypto/src/asn1/pkcs/CertificationRequestInfo.cs b/crypto/src/asn1/pkcs/CertificationRequestInfo.cs index d57753235..6d980131e 100644 --- a/crypto/src/asn1/pkcs/CertificationRequestInfo.cs +++ b/crypto/src/asn1/pkcs/CertificationRequestInfo.cs @@ -1,7 +1,6 @@ using System; using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1.Pkcs { @@ -31,20 +30,13 @@ namespace Org.BouncyCastle.Asn1.Pkcs internal SubjectPublicKeyInfo subjectPKInfo; internal Asn1Set attributes; - public static CertificationRequestInfo GetInstance( - object obj) + public static CertificationRequestInfo GetInstance(object obj) { if (obj is CertificationRequestInfo) - { - return (CertificationRequestInfo) obj; - } - - if (obj is Asn1Sequence) - { - return new CertificationRequestInfo((Asn1Sequence) obj); - } - - throw new ArgumentException("Unknown object in factory: " + Platform.GetTypeName(obj), "obj"); + return (CertificationRequestInfo)obj; + if (obj != null) + return new CertificationRequestInfo(Asn1Sequence.GetInstance(obj)); + return null; } public CertificationRequestInfo( @@ -56,7 +48,9 @@ namespace Org.BouncyCastle.Asn1.Pkcs this.subjectPKInfo = pkInfo; this.attributes = attributes; - if (subject == null || version == null || subjectPKInfo == null) + ValidateAttributes(attributes); + + if (subject == null || version == null || subjectPKInfo == null) { throw new ArgumentException( "Not all mandatory fields set in CertificationRequestInfo generator."); @@ -81,7 +75,9 @@ namespace Org.BouncyCastle.Asn1.Pkcs attributes = Asn1Set.GetInstance(tagobj, false); } - if (subject == null || version == null || subjectPKInfo == null) + ValidateAttributes(attributes); + + if (subject == null || version == null || subjectPKInfo == null) { throw new ArgumentException( "Not all mandatory fields set in CertificationRequestInfo generator."); @@ -120,5 +116,22 @@ namespace Org.BouncyCastle.Asn1.Pkcs return new DerSequence(v); } + + private static void ValidateAttributes(Asn1Set attributes) + { + if (attributes == null) + return; + + foreach (Asn1Encodable ae in attributes) + { + Asn1Object obj = ae.ToAsn1Object(); + AttributePkcs attr = AttributePkcs.GetInstance(obj); + if (attr.AttrType.Equals(PkcsObjectIdentifiers.Pkcs9AtChallengePassword)) + { + if (attr.AttrValues.Count != 1) + throw new ArgumentException("challengePassword attribute must have one value"); + } + } + } } } diff --git a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs index 042911a06..1a9a03e9f 100644 --- a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs +++ b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs @@ -9,23 +9,28 @@ namespace Org.BouncyCastle.Asn1.Pkcs // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } // public const string Pkcs1 = "1.2.840.113549.1.1"; + internal static readonly DerObjectIdentifier Pkcs1Oid = new DerObjectIdentifier(Pkcs1); + + public static readonly DerObjectIdentifier RsaEncryption = Pkcs1Oid.Branch("1"); + public static readonly DerObjectIdentifier MD2WithRsaEncryption = Pkcs1Oid.Branch("2"); + public static readonly DerObjectIdentifier MD4WithRsaEncryption = Pkcs1Oid.Branch("3"); + public static readonly DerObjectIdentifier MD5WithRsaEncryption = Pkcs1Oid.Branch("4"); + public static readonly DerObjectIdentifier Sha1WithRsaEncryption = Pkcs1Oid.Branch("5"); + public static readonly DerObjectIdentifier SrsaOaepEncryptionSet = Pkcs1Oid.Branch("6"); + public static readonly DerObjectIdentifier IdRsaesOaep = Pkcs1Oid.Branch("7"); + public static readonly DerObjectIdentifier IdMgf1 = Pkcs1Oid.Branch("8"); + public static readonly DerObjectIdentifier IdPSpecified = Pkcs1Oid.Branch("9"); + public static readonly DerObjectIdentifier IdRsassaPss = Pkcs1Oid.Branch("10"); + public static readonly DerObjectIdentifier Sha256WithRsaEncryption = Pkcs1Oid.Branch("11"); + public static readonly DerObjectIdentifier Sha384WithRsaEncryption = Pkcs1Oid.Branch("12"); + public static readonly DerObjectIdentifier Sha512WithRsaEncryption = Pkcs1Oid.Branch("13"); + public static readonly DerObjectIdentifier Sha224WithRsaEncryption = Pkcs1Oid.Branch("14"); + /** PKCS#1: 1.2.840.113549.1.1.15 */ + public static readonly DerObjectIdentifier Sha512_224WithRSAEncryption = Pkcs1Oid.Branch("15"); + /** PKCS#1: 1.2.840.113549.1.1.16 */ + public static readonly DerObjectIdentifier Sha512_256WithRSAEncryption = Pkcs1Oid.Branch("16"); - public static readonly DerObjectIdentifier RsaEncryption = new DerObjectIdentifier(Pkcs1 + ".1"); - public static readonly DerObjectIdentifier MD2WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".2"); - public static readonly DerObjectIdentifier MD4WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".3"); - public static readonly DerObjectIdentifier MD5WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".4"); - public static readonly DerObjectIdentifier Sha1WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".5"); - public static readonly DerObjectIdentifier SrsaOaepEncryptionSet = new DerObjectIdentifier(Pkcs1 + ".6"); - public static readonly DerObjectIdentifier IdRsaesOaep = new DerObjectIdentifier(Pkcs1 + ".7"); - public static readonly DerObjectIdentifier IdMgf1 = new DerObjectIdentifier(Pkcs1 + ".8"); - public static readonly DerObjectIdentifier IdPSpecified = new DerObjectIdentifier(Pkcs1 + ".9"); - public static readonly DerObjectIdentifier IdRsassaPss = new DerObjectIdentifier(Pkcs1 + ".10"); - public static readonly DerObjectIdentifier Sha256WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".11"); - public static readonly DerObjectIdentifier Sha384WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".12"); - public static readonly DerObjectIdentifier Sha512WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".13"); - public static readonly DerObjectIdentifier Sha224WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".14"); - - // + // // pkcs-3 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 } // @@ -195,6 +200,7 @@ namespace Org.BouncyCastle.Asn1.Pkcs // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)} // public const string IdAA = "1.2.840.113549.1.9.16.2"; + public static readonly DerObjectIdentifier IdAAOid = new DerObjectIdentifier(IdAA); public static readonly DerObjectIdentifier IdAAContentHint = new DerObjectIdentifier(IdAA + ".4"); // See RFC 2634 public static readonly DerObjectIdentifier IdAAMsgSigDigest = new DerObjectIdentifier(IdAA + ".5"); @@ -229,6 +235,20 @@ namespace Org.BouncyCastle.Asn1.Pkcs public static readonly DerObjectIdentifier IdAAEtsCertCrlTimestamp = new DerObjectIdentifier(IdAA + ".26"); public static readonly DerObjectIdentifier IdAAEtsArchiveTimestamp = new DerObjectIdentifier(IdAA + ".27"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.37 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.5">RFC 4108</a> */ + public static readonly DerObjectIdentifier IdAADecryptKeyID = IdAAOid.Branch("37"); + + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.38 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.6">RFC 4108</a> */ + public static readonly DerObjectIdentifier IdAAImplCryptoAlgs = IdAAOid.Branch("38"); + + /** PKCS#9: 1.2.840.113549.1.9.16.2.54 <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/ + public static readonly DerObjectIdentifier IdAAAsymmDecryptKeyID = IdAAOid.Branch("54"); + + /** PKCS#9: 1.2.840.113549.1.9.16.2.43 <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/ + public static readonly DerObjectIdentifier IdAAImplCompressAlgs = IdAAOid.Branch("43"); + /** PKCS#9: 1.2.840.113549.1.9.16.2.40 <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/ + public static readonly DerObjectIdentifier IdAACommunityIdentifiers = IdAAOid.Branch("40"); + [Obsolete("Use 'IdAAEtsSigPolicyID' instead")] public static readonly DerObjectIdentifier IdAASigPolicyID = IdAAEtsSigPolicyID; [Obsolete("Use 'IdAAEtsCommitmentType' instead")] diff --git a/crypto/src/asn1/x509/KeyPurposeId.cs b/crypto/src/asn1/x509/KeyPurposeId.cs index 4b48a9b51..1a564b97a 100644 --- a/crypto/src/asn1/x509/KeyPurposeId.cs +++ b/crypto/src/asn1/x509/KeyPurposeId.cs @@ -32,5 +32,7 @@ namespace Org.BouncyCastle.Asn1.X509 // microsoft key purpose ids // public static readonly KeyPurposeID IdKPSmartCardLogon = new KeyPurposeID("1.3.6.1.4.1.311.20.2.2"); + + public static readonly KeyPurposeID IdKPMacAddress = new KeyPurposeID("1.3.6.1.1.1.1.22"); } } diff --git a/crypto/src/asn1/x509/X509Extensions.cs b/crypto/src/asn1/x509/X509Extensions.cs index 2ef73f629..049d728bb 100644 --- a/crypto/src/asn1/x509/X509Extensions.cs +++ b/crypto/src/asn1/x509/X509Extensions.cs @@ -164,6 +164,11 @@ namespace Org.BouncyCastle.Asn1.X509 */ public static readonly DerObjectIdentifier TargetInformation = new DerObjectIdentifier("2.5.29.55"); + /** + * Expired Certificates on CRL extension + */ + public static readonly DerObjectIdentifier ExpiredCertsOnCrl = new DerObjectIdentifier("2.5.29.60"); + private readonly IDictionary extensions = Platform.CreateHashtable(); private readonly IList ordering; diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs index 1f0e412d8..7a5066ec3 100644 --- a/crypto/src/bcpg/ArmoredOutputStream.cs +++ b/crypto/src/bcpg/ArmoredOutputStream.cs @@ -20,6 +20,8 @@ namespace Org.BouncyCastle.Bcpg public class ArmoredOutputStream : BaseOutputStream { + public static readonly string HeaderVersion = "Version"; + private static readonly byte[] encodingTable = { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', @@ -101,44 +103,58 @@ namespace Org.BouncyCastle.Bcpg private static readonly string footerStart = "-----END PGP "; private static readonly string footerTail = "-----"; - private static readonly string version = "BCPG C# v" + AssemblyInfo.Version; + private static readonly string Version = "BCPG C# v" + AssemblyInfo.Version; private readonly IDictionary headers; public ArmoredOutputStream(Stream outStream) { this.outStream = outStream; - this.headers = Platform.CreateHashtable(); - this.headers["Version"] = version; + this.headers = Platform.CreateHashtable(1); + this.headers.Add(HeaderVersion, Version); } public ArmoredOutputStream(Stream outStream, IDictionary headers) { this.outStream = outStream; this.headers = Platform.CreateHashtable(headers); - this.headers["Version"] = version; + if (!this.headers.Contains(HeaderVersion)) + { + this.headers.Add(HeaderVersion, Version); + } } /** - * Set an additional header entry. + * Set an additional header entry. A null value will clear the entry for name. * * @param name the name of the header entry. * @param v the value of the header entry. */ - public void SetHeader( - string name, - string v) + public void SetHeader(string name, string v) { - headers[name] = v; + if (v == null) + { + headers.Remove(name); + } + else + { + headers[name] = v; + } } /** - * Reset the headers to only contain a Version string. + * Reset the headers to only contain a Version string (if one is present). */ public void ResetHeaders() { + string version = (string)headers[HeaderVersion]; + headers.Clear(); - headers["Version"] = version; + + if (version != null) + { + headers[HeaderVersion] = Version; + } } /** @@ -248,14 +264,17 @@ namespace Org.BouncyCastle.Bcpg } DoWrite(headerStart + type + headerTail + nl); - WriteHeaderEntry("Version", (string) headers["Version"]); + if (headers.Contains(HeaderVersion)) + { + WriteHeaderEntry(HeaderVersion, (string)headers[HeaderVersion]); + } foreach (DictionaryEntry de in headers) { - string k = (string) de.Key; - if (k != "Version") + string k = (string)de.Key; + if (k != HeaderVersion) { - string v = (string) de.Value; + string v = (string)de.Value; WriteHeaderEntry(k, v); } } diff --git a/crypto/src/crypto/agreement/DHAgreement.cs b/crypto/src/crypto/agreement/DHAgreement.cs index d214caafe..e988c0d53 100644 --- a/crypto/src/crypto/agreement/DHAgreement.cs +++ b/crypto/src/crypto/agreement/DHAgreement.cs @@ -81,13 +81,19 @@ namespace Org.BouncyCastle.Crypto.Agreement throw new ArgumentNullException("message"); if (!pub.Parameters.Equals(dhParams)) - { throw new ArgumentException("Diffie-Hellman public key has wrong parameters."); - } - BigInteger p = dhParams.P; + BigInteger p = dhParams.P; - return message.ModPow(key.X, p).Multiply(pub.Y.ModPow(privateValue, p)).Mod(p); - } - } + BigInteger peerY = pub.Y; + if (peerY == null || peerY.CompareTo(BigInteger.One) <= 0 || peerY.CompareTo(p.Subtract(BigInteger.One)) >= 0) + throw new ArgumentException("Diffie-Hellman public key is weak"); + + BigInteger result = peerY.ModPow(privateValue, p); + if (result.Equals(BigInteger.One)) + throw new InvalidOperationException("Shared key can't be 1"); + + return message.ModPow(key.X, p).Multiply(result).Mod(p); + } + } } diff --git a/crypto/src/crypto/agreement/DHBasicAgreement.cs b/crypto/src/crypto/agreement/DHBasicAgreement.cs index 75b5e9db5..6c3fe6595 100644 --- a/crypto/src/crypto/agreement/DHBasicAgreement.cs +++ b/crypto/src/crypto/agreement/DHBasicAgreement.cs @@ -54,11 +54,19 @@ namespace Org.BouncyCastle.Crypto.Agreement DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey; if (!pub.Parameters.Equals(dhParams)) - { throw new ArgumentException("Diffie-Hellman public key has wrong parameters."); - } - return pub.Y.ModPow(key.X, dhParams.P); + BigInteger p = dhParams.P; + + BigInteger peerY = pub.Y; + if (peerY == null || peerY.CompareTo(BigInteger.One) <= 0 || peerY.CompareTo(p.Subtract(BigInteger.One)) >= 0) + throw new ArgumentException("Diffie-Hellman public key is weak"); + + BigInteger result = peerY.ModPow(key.X, p); + if (result.Equals(BigInteger.One)) + throw new InvalidOperationException("Shared key can't be 1"); + + return result; } } } diff --git a/crypto/src/crypto/agreement/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs index 93b65af98..425a9784c 100644 --- a/crypto/src/crypto/agreement/DHStandardGroups.cs +++ b/crypto/src/crypto/agreement/DHStandardGroups.cs @@ -161,6 +161,11 @@ namespace Org.BouncyCastle.Crypto.Agreement + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" + "855E6EEB22B3B2E5"; private static readonly string rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353"; + + /// <remarks> + /// Existence of a "hidden SNFS" backdoor cannot be ruled out. see https://eprint.iacr.org/2016/961.pdf . + /// </remarks> + [Obsolete("Existence of a 'hidden SNFS' backdoor cannot be ruled out.")] public static readonly DHParameters rfc5114_1024_160 = FromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g, rfc5114_1024_160_q); @@ -177,6 +182,11 @@ namespace Org.BouncyCastle.Crypto.Agreement + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269" + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA"; private static readonly string rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB"; + + /// <remarks> + /// Existence of a "hidden SNFS" backdoor cannot be ruled out. see https://eprint.iacr.org/2016/961.pdf . + /// </remarks> + [Obsolete("Existence of a 'hidden SNFS' backdoor cannot be ruled out.")] public static readonly DHParameters rfc5114_2048_224 = FromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g, rfc5114_2048_224_q); @@ -194,6 +204,11 @@ namespace Org.BouncyCastle.Crypto.Agreement + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659"; private static readonly string rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B" + "A308B0FE64F5FBD3"; + + /// <remarks> + /// Existence of a "hidden SNFS" backdoor cannot be ruled out. see https://eprint.iacr.org/2016/961.pdf . + /// </remarks> + [Obsolete("Existence of a 'hidden SNFS' backdoor cannot be ruled out.")] public static readonly DHParameters rfc5114_2048_256 = FromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g, rfc5114_2048_256_q); diff --git a/crypto/src/crypto/digests/DSTU7564Digest.cs b/crypto/src/crypto/digests/DSTU7564Digest.cs new file mode 100644 index 000000000..9a785a5c9 --- /dev/null +++ b/crypto/src/crypto/digests/DSTU7564Digest.cs @@ -0,0 +1,550 @@ +using System; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +//using Org.BouncyCastle.Utilities; + + +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of Ukrainian DSTU 7564 hash function + */ + public class Dstu7564Digest : IDigest, IMemoable + { + private const int ROWS = 8; + private const int REDUCTION_POLYNOMIAL = 0x011d; + private const int BITS_IN_BYTE = 8; + + + private const int NB_512 = 8; //Number of 8-byte words in state for <=256-bit hash code. + private const int NB_1024 = 16; //Number of 8-byte words in state for <=512-bit hash code. + + private const int NR_512 = 10; //Number of rounds for 512-bit state. + private const int NR_1024 = 14; //Number of rounds for 1024-bit state. + + private const int STATE_BYTE_SIZE_512 = ROWS * NB_512; + private const int STATE_BYTE_SIZE_1024 = ROWS * NB_1024; + + private int hashSize; + private int blockSize; + + + + private int columns; + private int rounds; + + + private byte[] padded_; + + private byte[][] state_; + + private ulong inputLength; + private int bufOff; + private byte[] buf; + + public Dstu7564Digest(Dstu7564Digest digest) + { + copyIn(digest); + } + + private void copyIn(Dstu7564Digest digest) + { + this.hashSize = digest.hashSize; + this.blockSize = digest.blockSize; + + this.columns = digest.columns; + this.rounds = digest.rounds; + + this.padded_ = Arrays.Clone(digest.padded_); + this.state_ = new byte[digest.state_.Length][]; + for (int i = 0; i != this.state_.Length; i++) + { + this.state_[i] = Arrays.Clone(digest.state_[i]); + } + + this.inputLength = digest.inputLength; + this.bufOff = digest.bufOff; + this.buf = Arrays.Clone(digest.buf); + } + + public Dstu7564Digest(int hashSizeBits) + { + if (hashSizeBits == 256 || hashSizeBits == 384 || hashSizeBits == 512) + { + this.hashSize = hashSizeBits / 8; + } + else + { + throw new ArgumentException("Hash size is not recommended. Use 256 or 384 or 512 size"); + } + + if (hashSizeBits > 256) + { + this.blockSize = 1024 / 8; + this.columns = NB_1024; + this.rounds = NR_1024; + this.state_ = new byte[STATE_BYTE_SIZE_1024][]; + + } + else + { + this.blockSize = 512 / 8; + this.columns = NB_512; + this.rounds = NR_512; + this.state_ = new byte[STATE_BYTE_SIZE_512][]; + + } + + //Console.WriteLine("length: " + state_.Length); + + for (int i = 0; i < state_.Length; i++) + { + this.state_[i] = new byte[columns]; + } + + this.state_[0][0] = (byte)state_.Length; + + this.hashSize = hashSizeBits / 8; + + this.padded_ = null; + this.buf = new byte[blockSize]; + } + + public string AlgorithmName + { + get { return "DSTU7564"; } + } + + + public virtual void BlockUpdate(byte[] input, int inOff, int length) + { + while (bufOff != 0 && length > 0) + { + Update(input[inOff++]); + length--; + } + + if (length > 0) + { + while (length > blockSize) + { + ProcessBlock(input, inOff); + inOff += blockSize; + inputLength += (ulong)blockSize; + length -= blockSize; + } + + while (length > 0) + { + Update(input[inOff++]); + length--; + } + } + } + + protected byte[] Pad(byte[] input, int inOff, int length) + { + //Console.WriteLine(length); + + byte[] padded; + if (blockSize - length < 13) // terminator byte + 96 bits of length + { + padded = new byte[2 * blockSize]; + } + else + { + padded = new byte[blockSize]; + } + + + Array.Copy(input, inOff, padded, 0, length); + padded[length] = 0x80; + Pack.UInt64_To_LE(inputLength * 8, padded, padded.Length - 12); + + return padded; + } + + protected void ProcessBlock(byte[] input, int inOff) + { + byte[][] temp1 = new byte[STATE_BYTE_SIZE_1024][]; + byte[][] temp2 = new byte[STATE_BYTE_SIZE_1024][]; + + for (int i = 0; i < state_.Length; i++) + { + temp1[i] = new byte[ROWS]; + temp2[i] = new byte[ROWS]; + } + + for (int i = 0; i < ROWS; ++i) + { + for (int j = 0; j < columns; ++j) + { + //Console.WriteLine("row = {0}, column = {1}", i, j); + + temp1[j][i] = (byte)(state_[j][i] ^ input[j * ROWS + i + inOff]); + temp2[j][i] = input[j * ROWS + i + inOff]; + + } + + } + + P(temp1); + + Q(temp2); + + for (int i = 0; i < ROWS; ++i) + { + for (int j = 0; j < columns; ++j) + { + state_[j][i] ^= (byte)(temp1[j][i] ^ temp2[j][i]); + + } + + } + } + + public int DoFinal(byte[] output, int outOff) + { + padded_ = Pad(buf, 0, bufOff); + + ProcessBlock(padded_, 0); + + + //Console.WriteLine(stateLine.Length); + + byte[][] temp = new byte[STATE_BYTE_SIZE_1024][]; + for (int i = 0; i < state_.Length; i++) + { + temp[i] = new byte[ROWS]; + Array.Copy(state_[i], temp[i], ROWS); + } + + P(temp); + + for (int i = 0; i < ROWS; ++i) + { + for (int j = 0; j < columns; ++j) + { + state_[j][i] ^= temp[j][i]; + //Console.Write("{0:x} ", state_[j][i]); + } + //Console.WriteLine(); + } + + byte[] stateLine = new byte[ROWS * columns]; + int stateLineIndex = 0; + for (int j = 0; j < columns; ++j) + { + for (int i = 0; i < ROWS; ++i) + { + + stateLine[stateLineIndex] = state_[j][i]; + stateLineIndex++; + + //Console.WriteLine("index = {0}, row = {1}, column = {2}", stateLineIndex, i, j); + + } + } + + //Console.WriteLine("final: " + Hex.ToHexString(stateLine)); + //Console.WriteLine(stateLine.Length); + + Array.Copy(stateLine, stateLine.Length - hashSize, output, outOff, hashSize); + + Reset(); + + return hashSize; + } + + public void Reset() + { + for (int bufferIndex = 0; bufferIndex < state_.Length; bufferIndex++) + { + state_[bufferIndex] = new byte[columns]; + } + + state_[0][0] = (byte)state_.Length; + + inputLength = 0; + bufOff = 0; + + Arrays.Fill(buf, (byte)0); + Arrays.Fill(padded_, (byte)0); + } + + public int GetDigestSize() + { + return hashSize; + } + + public int GetByteLength() + { + return blockSize; + } + + public void Update(byte input) + { + buf[bufOff++] = input; + if (bufOff == blockSize) + { + ProcessBlock(buf, 0); + bufOff = 0; + } + inputLength++; + } + + void SubBytes(byte[][] state) + { + int i, j; + for (i = 0; i < ROWS; ++i) + { + for (j = 0; j < columns; ++j) + { + state[j][i] = sBoxes[i % 4][state[j][i]]; + } + } + } + + void ShiftBytes(byte[][] state) + { + int i, j; + byte[] temp = new byte[NB_1024]; + int shift = -1; + for (i = 0; i < ROWS; ++i) + { + if ((i == ROWS - 1) && (columns == NB_1024)) + { + shift = 11; + } + else + { + ++shift; + } + for (j = 0; j < columns; ++j) + { + temp[(j + shift) % columns] = state[j][i]; + } + for (j = 0; j < columns; ++j) + { + state[j][i] = temp[j]; + } + } + } + + byte MultiplyGF(byte x, byte y) + { + int i; + byte r = 0; + byte hbit = 0; + for (i = 0; i < BITS_IN_BYTE; ++i) + { + if ((y & 0x1) == 1) + { + r ^= x; + } + + hbit = (byte)(x & 0x80); + + x <<= 1; + + if (hbit == 0x80) + { + x = (byte)((int)x ^ REDUCTION_POLYNOMIAL); + } + + y >>= 1; + } + return r; + } + + private void MixColumns(byte[][] state) + { + int i, row, col, b; + byte product; + byte[] result = new byte[ROWS]; + + for (col = 0; col < columns; ++col) + { + Array.Clear(result, 0, ROWS); + for (row = ROWS - 1; row >= 0; --row) + { + product = 0; + for (b = ROWS - 1; b >= 0; --b) + { + product ^= MultiplyGF(state[col][b], mds_matrix[row][b]); + } + result[row] = product; + } + for (i = 0; i < ROWS; ++i) + { + state[col][i] = result[i]; + } + } + } + + void AddRoundConstantP(byte[][] state, int round) + { + int i; + for (i = 0; i < columns; ++i) + { + state[i][0] ^= (byte)((i * 0x10) ^ round); + } + } + + void AddRoundConstantQ(byte[][] state, int round) + { + int j; + UInt64[] s = new UInt64[columns]; + + for (j = 0; j < columns; j++) + { + s[j] = Pack.LE_To_UInt64(state[j]); + + s[j] = s[j] + (0x00F0F0F0F0F0F0F3UL ^ ((((UInt64)(columns - j - 1) * 0x10UL) ^ (UInt64)round) << (7 * 8))); + + state[j] = Pack.UInt64_To_LE(s[j]); + } + } + + void P(byte[][] state) + { + int i; + for (i = 0; i < rounds; ++i) + { + AddRoundConstantP(state, i); + SubBytes(state); + ShiftBytes(state); + MixColumns(state); + } + } + + void Q(byte[][] state) + { + int i; + for (i = 0; i < rounds; ++i) + { + AddRoundConstantQ(state, i); + SubBytes(state); + ShiftBytes(state); + MixColumns(state); + } + } + + public IMemoable Copy() + { + return new Dstu7564Digest(this); + } + + public void Reset(IMemoable other) + { + Dstu7564Digest d = (Dstu7564Digest)other; + + copyIn(d); + } + + private readonly byte[][] mds_matrix = new byte[][] + { + new byte[] {0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04}, + new byte[] {0x04, 0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07}, + new byte[] {0x07, 0x04, 0x01, 0x01, 0x05, 0x01, 0x08, 0x06}, + new byte[] {0x06, 0x07, 0x04, 0x01, 0x01, 0x05, 0x01, 0x08}, + new byte[] {0x08, 0x06, 0x07, 0x04, 0x01, 0x01, 0x05, 0x01}, + new byte[] {0x01, 0x08, 0x06, 0x07, 0x04, 0x01, 0x01, 0x05}, + new byte[] {0x05, 0x01, 0x08, 0x06, 0x07, 0x04, 0x01, 0x01}, + new byte[] {0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04, 0x01} + }; + + + + + private readonly byte[][] sBoxes = new byte[][] + { + new byte[] + { + 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09, + 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39, + 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6, + 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1, + 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27, + 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41, + 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e, + 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55, + 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff, + 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1, + 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76, + 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26, + 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82, + 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8, + 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d, + 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80 + }, + + new byte[] + { + 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8, + 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d, + 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d, + 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc, + 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee, + 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca, + 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20, + 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f, + 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51, + 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98, + 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9, + 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05, + 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82, + 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad, + 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59, + 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7 + }, + + new byte[] + { + 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59, + 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1, + 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72, + 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90, + 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35, + 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48, + 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38, + 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33, + 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29, + 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83, + 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2, + 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43, + 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82, + 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91, + 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44, + 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67 + }, + + new byte[] + { + 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f, + 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd, + 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66, + 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf, + 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99, + 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71, + 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60, + 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b, + 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09, + 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2, + 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7, + 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a, + 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39, + 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef, + 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36, + 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61 + } + }; + + + } +} diff --git a/crypto/src/crypto/encodings/OaepEncoding.cs b/crypto/src/crypto/encodings/OaepEncoding.cs index cb23b1710..287876f12 100644 --- a/crypto/src/crypto/encodings/OaepEncoding.cs +++ b/crypto/src/crypto/encodings/OaepEncoding.cs @@ -3,6 +3,7 @@ using System; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Encodings { @@ -13,7 +14,6 @@ namespace Org.BouncyCastle.Crypto.Encodings : IAsymmetricBlockCipher { private byte[] defHash; - private IDigest hash; private IDigest mgf1Hash; private IAsymmetricBlockCipher engine; @@ -48,10 +48,11 @@ namespace Org.BouncyCastle.Crypto.Encodings byte[] encodingParams) { this.engine = cipher; - this.hash = hash; this.mgf1Hash = mgf1Hash; this.defHash = new byte[hash.GetDigestSize()]; + hash.Reset(); + if (encodingParams != null) { hash.BlockUpdate(encodingParams, 0, encodingParams.Length); @@ -137,6 +138,8 @@ namespace Org.BouncyCastle.Crypto.Encodings int inOff, int inLen) { + Check.DataLength(inLen > GetInputBlockSize(), "input data too long"); + byte[] block = new byte[GetInputBlockSize() + 1 + 2 * defHash.Length]; // @@ -202,28 +205,17 @@ namespace Org.BouncyCastle.Crypto.Encodings int inLen) { byte[] data = engine.ProcessBlock(inBytes, inOff, inLen); - byte[] block; + byte[] block = new byte[engine.GetOutputBlockSize()]; // // as we may have zeros in our leading bytes for the block we produced // on encryption, we need to make sure our decrypted block comes back // the same size. // - if (data.Length < engine.GetOutputBlockSize()) - { - block = new byte[engine.GetOutputBlockSize()]; - Array.Copy(data, 0, block, block.Length - data.Length, data.Length); - } - else - { - block = data; - } + Array.Copy(data, 0, block, block.Length - data.Length, data.Length); - if (block.Length < (2 * defHash.Length) + 1) - { - throw new InvalidCipherTextException("data too short"); - } + bool shortData = (block.Length < (2 * defHash.Length) + 1); // // unmask the seed. @@ -250,36 +242,39 @@ namespace Org.BouncyCastle.Crypto.Encodings // check the hash of the encoding params. // long check to try to avoid this been a source of a timing attack. // + bool defHashWrong = false; + + for (int i = 0; i != defHash.Length; i++) { - int diff = 0; - for (int i = 0; i < defHash.Length; ++i) + if (defHash[i] != block[defHash.Length + i]) { - diff |= (byte)(defHash[i] ^ block[defHash.Length + i]); + defHashWrong = true; } - - if (diff != 0) - throw new InvalidCipherTextException("data hash wrong"); } // // find the data block // - int start; - for (start = 2 * defHash.Length; start != block.Length; start++) + int start = block.Length; + + for (int index = 2 * defHash.Length; index != block.Length; index++) { - if (block[start] != 0) + if (block[index] != 0 & start == block.Length) { - break; + start = index; } } - if (start > (block.Length - 1) || block[start] != 1) - { - throw new InvalidCipherTextException("data start wrong " + start); - } + bool dataStartWrong = (start > (block.Length - 1) | block[start] != 1); start++; + if (defHashWrong | shortData | dataStartWrong) + { + Arrays.Fill(block, 0); + throw new InvalidCipherTextException("data wrong"); + } + // // extract the data block // @@ -317,9 +312,9 @@ namespace Org.BouncyCastle.Crypto.Encodings byte[] C = new byte[4]; int counter = 0; - hash.Reset(); + mgf1Hash.Reset(); - do + while (counter < (length / hashBuf.Length)) { ItoOSP(counter, C); @@ -328,8 +323,9 @@ namespace Org.BouncyCastle.Crypto.Encodings mgf1Hash.DoFinal(hashBuf, 0); Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, hashBuf.Length); + + counter++; } - while (++counter < (length / hashBuf.Length)); if ((counter * hashBuf.Length) < length) { diff --git a/crypto/src/crypto/encodings/Pkcs1Encoding.cs b/crypto/src/crypto/encodings/Pkcs1Encoding.cs index 35fd96abe..b2d60fe4c 100644 --- a/crypto/src/crypto/encodings/Pkcs1Encoding.cs +++ b/crypto/src/crypto/encodings/Pkcs1Encoding.cs @@ -45,16 +45,18 @@ namespace Org.BouncyCastle.Crypto.Encodings } - private SecureRandom random; - private IAsymmetricBlockCipher engine; - private bool forEncryption; - private bool forPrivateKey; - private bool useStrictLength; - private int pLen = -1; - private byte[] fallback = null; + private SecureRandom random; + private IAsymmetricBlockCipher engine; + private bool forEncryption; + private bool forPrivateKey; + private bool useStrictLength; + private int pLen = -1; + private byte[] fallback = null; + private byte[] blockBuffer = null; /** * Basic constructor. + * * @param cipher */ public Pkcs1Encoding( @@ -104,9 +106,7 @@ namespace Org.BouncyCastle.Crypto.Encodings get { return engine.AlgorithmName + "/PKCS1Padding"; } } - public void Init( - bool forEncryption, - ICipherParameters parameters) + public void Init(bool forEncryption, ICipherParameters parameters) { AsymmetricKeyParameter kParam; if (parameters is ParametersWithRandom) @@ -126,6 +126,10 @@ namespace Org.BouncyCastle.Crypto.Encodings this.forPrivateKey = kParam.IsPrivate; this.forEncryption = forEncryption; + this.blockBuffer = new byte[engine.GetOutputBlockSize()]; + + if (pLen > 0 && fallback == null && random == null) + throw new ArgumentException("encoder requires random"); } public int GetInputBlockSize() @@ -255,7 +259,6 @@ namespace Org.BouncyCastle.Crypto.Encodings * @param inLen Length of the encrypted block. * @param pLen Length of the desired output. * @return The plaintext without padding, or a random value if the padding was incorrect. - * * @throws InvalidCipherTextException */ private byte[] DecodeBlockOrRandom(byte[] input, int inOff, int inLen) @@ -264,7 +267,7 @@ namespace Org.BouncyCastle.Crypto.Encodings throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing"); byte[] block = engine.ProcessBlock(input, inOff, inLen); - byte[] random = null; + byte[] random; if (this.fallback == null) { random = new byte[this.pLen]; @@ -275,37 +278,25 @@ namespace Org.BouncyCastle.Crypto.Encodings random = fallback; } - /* - * TODO: This is a potential dangerous side channel. However, you can - * fix this by changing the RSA engine in a way, that it will always - * return blocks of the same length and prepend them with 0 bytes if - * needed. - */ - if (block.Length < GetOutputBlockSize()) - throw new InvalidCipherTextException("block truncated"); + byte[] data = (useStrictLength & (block.Length != engine.GetOutputBlockSize())) ? blockBuffer : block; - /* - * TODO: Potential side channel. Fix it by making the engine always - * return blocks of the correct length. - */ - if (useStrictLength && block.Length != engine.GetOutputBlockSize()) - throw new InvalidCipherTextException("block incorrect size"); - - /* - * Check the padding. - */ - int correct = Pkcs1Encoding.CheckPkcs1Encoding(block, this.pLen); + /* + * Check the padding. + */ + int correct = CheckPkcs1Encoding(data, this.pLen); - /* - * Now, to a constant time constant memory copy of the decrypted value - * or the random value, depending on the validity of the padding. - */ + /* + * Now, to a constant time constant memory copy of the decrypted value + * or the random value, depending on the validity of the padding. + */ byte[] result = new byte[this.pLen]; for (int i = 0; i < this.pLen; i++) { - result[i] = (byte)((block[i+(block.Length-pLen)]&(~correct)) | (random[i]&correct)); + result[i] = (byte)((data[i + (data.Length - pLen)] & (~correct)) | (random[i] & correct)); } + Arrays.Fill(data, 0); + return result; } @@ -327,56 +318,67 @@ namespace Org.BouncyCastle.Crypto.Encodings } byte[] block = engine.ProcessBlock(input, inOff, inLen); + bool incorrectLength = (useStrictLength & (block.Length != engine.GetOutputBlockSize())); + byte[] data; if (block.Length < GetOutputBlockSize()) { - throw new InvalidCipherTextException("block truncated"); + data = blockBuffer; } - - byte type = block[0]; - - if (type != 1 && type != 2) + else { - throw new InvalidCipherTextException("unknown block type"); + data = block; } - if (useStrictLength && block.Length != engine.GetOutputBlockSize()) - { - throw new InvalidCipherTextException("block incorrect size"); - } + byte expectedType = (byte)(forPrivateKey ? 2 : 1); + byte type = data[0]; + + bool badType = (type != expectedType); // // find and extract the message block. // - int start; - for (start = 1; start != block.Length; start++) - { - byte pad = block[start]; + int start = FindStart(type, data); - if (pad == 0) - { - break; - } + start++; // data should start at the next byte - if (type == 1 && pad != (byte)0xff) - { - throw new InvalidCipherTextException("block padding incorrect"); - } + if (badType | (start < HeaderLength)) + { + Arrays.Fill(data, 0); + throw new InvalidCipherTextException("block incorrect"); } - start++; // data should start at the next byte - - if (start > block.Length || start < HeaderLength) + // if we get this far, it's likely to be a genuine encoding error + if (incorrectLength) { - throw new InvalidCipherTextException("no data in block"); + Arrays.Fill(data, 0); + throw new InvalidCipherTextException("block incorrect size"); } - byte[] result = new byte[block.Length - start]; + byte[] result = new byte[data.Length - start]; - Array.Copy(block, start, result, 0, result.Length); + Array.Copy(data, start, result, 0, result.Length); return result; } - } + private int FindStart(byte type, byte[] block) + { + int start = -1; + bool padErr = false; + + for (int i = 1; i != block.Length; i++) + { + byte pad = block[i]; + + if (pad == 0 & start < 0) + { + start = i; + } + padErr |= ((type == 1) & (start < 0) & (pad != (byte)0xff)); + } + + return padErr ? -1 : start; + } + } } diff --git a/crypto/src/crypto/engines/AesEngine.cs b/crypto/src/crypto/engines/AesEngine.cs index ba62af4da..91bdf69ef 100644 --- a/crypto/src/crypto/engines/AesEngine.cs +++ b/crypto/src/crypto/engines/AesEngine.cs @@ -429,6 +429,8 @@ namespace Org.BouncyCastle.Crypto.Engines private uint C0, C1, C2, C3; private bool forEncryption; + private byte[] s; + private const int BLOCK_SIZE = 16; /** @@ -459,6 +461,7 @@ namespace Org.BouncyCastle.Crypto.Engines WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption); this.forEncryption = forEncryption; + this.s = Arrays.Clone(forEncryption ? S : Si); } public virtual string AlgorithmName @@ -560,10 +563,10 @@ namespace Org.BouncyCastle.Crypto.Engines // the final round's table is a simple function of S so we don't use a whole other four tables for it kw = KW[r]; - this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; - this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; - this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; - this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)s[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)s[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)s[r3 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; } private void DecryptBlock(uint[][] KW) @@ -598,10 +601,10 @@ namespace Org.BouncyCastle.Crypto.Engines // the final round's table is a simple function of Si so we don't use a whole other four tables for it kw = KW[0]; - this.C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; - this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; - this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; - this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; + this.C0 = (uint)Si[r0 & 255] ^ (((uint)s[(r3 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)s[r1 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r2 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)s[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)s[(r2 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[3]; } } } diff --git a/crypto/src/crypto/engines/AesFastEngine.cs b/crypto/src/crypto/engines/AesFastEngine.cs index 3a9c3a89e..9d3a86fd2 100644 --- a/crypto/src/crypto/engines/AesFastEngine.cs +++ b/crypto/src/crypto/engines/AesFastEngine.cs @@ -31,6 +31,11 @@ namespace Org.BouncyCastle.Crypto.Engines * This file contains the fast version with 8Kbytes of static tables for round precomputation * </p> */ + /// <remarks> + /// Unfortunately this class has a few side channel issues. + /// In an environment where encryption/decryption may be closely observed it should not be used. + /// </remarks> + [Obsolete("Use AesEngine instead")] public class AesFastEngine : IBlockCipher { diff --git a/crypto/src/crypto/engines/ChaCha7539Engine.cs b/crypto/src/crypto/engines/ChaCha7539Engine.cs index 5a12be016..af4163a02 100644 --- a/crypto/src/crypto/engines/ChaCha7539Engine.cs +++ b/crypto/src/crypto/engines/ChaCha7539Engine.cs @@ -19,7 +19,7 @@ namespace Org.BouncyCastle.Crypto.Engines public override string AlgorithmName { - get { return "ChaCha" + rounds; } + get { return "ChaCha7539" + rounds; } } protected override int NonceSize diff --git a/crypto/src/crypto/macs/DSTU7564Mac.cs b/crypto/src/crypto/macs/DSTU7564Mac.cs new file mode 100644 index 000000000..a5f984270 --- /dev/null +++ b/crypto/src/crypto/macs/DSTU7564Mac.cs @@ -0,0 +1,156 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /// <summary> + /// Implementation of DSTU7564 mac mode + /// </summary> + public class DSTU7564Mac : IMac + { + private Dstu7564Digest engine; + private int macSize; + + private ulong inputLength; + + byte[] paddedKey; + byte[] invertedKey; + byte[] paddedInput; + + public string AlgorithmName + { + get + { + return "DSTU7564Mac"; + } + } + + public DSTU7564Mac(int macSizeBits) + { + engine = new Dstu7564Digest(macSizeBits); + macSize = macSizeBits / 8; + } + + public void Init(ICipherParameters parameters) + { + if (parameters is KeyParameter) + { + byte[] key = ((KeyParameter)parameters).GetKey(); + + invertedKey = new byte[key.Length]; + + paddedKey = PadKey(key); + + for (int byteIndex = 0; byteIndex < invertedKey.Length; byteIndex++) + { + invertedKey[byteIndex] = (byte)(key[byteIndex] ^ (byte)0xFF); + } + } + else + { + throw new ArgumentException("Bad parameter passed"); + } + + engine.BlockUpdate(paddedKey, 0, paddedKey.Length); + } + + public int GetMacSize() + { + return macSize; + } + + public void BlockUpdate(byte[] input, int inOff, int len) + { + if (input.Length - inOff < len) + { + throw new DataLengthException("Input buffer too short"); + } + + if (paddedKey == null) + { + throw new InvalidOperationException(AlgorithmName + " not initialised"); + } + + engine.BlockUpdate(input, inOff, len); + inputLength += (ulong)len; + + } + + public void Update(byte input) + { + engine.Update(input); + inputLength++; + } + + public int DoFinal(byte[] output, int outOff) + { + if (output.Length - outOff < macSize) + { + throw new DataLengthException("Output buffer too short"); + } + if (paddedKey == null) + { + throw new InvalidOperationException(AlgorithmName + " not initialised"); + } + + Pad(); + + engine.BlockUpdate(invertedKey, 0, invertedKey.Length); + + inputLength = 0; + + return engine.DoFinal(output, outOff); + } + + public void Reset() + { + inputLength = 0; + engine.Reset(); + if (paddedKey != null) + { + engine.BlockUpdate(paddedKey, 0, paddedKey.Length); + } + } + + private void Pad() + { + int extra = engine.GetByteLength() - (int)(inputLength % (ulong)engine.GetByteLength()); + if (extra < 13) // terminator byte + 96 bits of length + { + extra += engine.GetByteLength(); + } + + byte[] padded = new byte[extra]; + + padded[0] = (byte)0x80; // Defined in standard; + + // Defined in standard; + Pack.UInt64_To_LE(inputLength * 8, padded, padded.Length - 12); + + engine.BlockUpdate(padded, 0, padded.Length); + } + + private byte[] PadKey(byte[] input) + { + int paddedLen = ((input.Length + engine.GetByteLength() - 1) / engine.GetByteLength()) *engine.GetByteLength(); + + int extra = engine.GetByteLength() - (int)(input.Length % engine.GetByteLength()); + if (extra < 13) // terminator byte + 96 bits of length + { + paddedLen += engine.GetByteLength(); + } + + byte[] padded = new byte[paddedLen]; + + Array.Copy(input, 0, padded, 0, input.Length); + + padded[input.Length] = (byte)0x80; // Defined in standard; + Pack.UInt32_To_LE((uint)(input.Length * 8), padded, padded.Length - 12); // Defined in standard; + + return padded; + } + } +} diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs index 9d940fe75..a6cd00401 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs @@ -23,7 +23,9 @@ namespace Org.BouncyCastle.Crypto.Modes // These fields are set by Init and not modified by processing private bool forEncryption; + private bool initialised; private int macSize; + private byte[] lastKey; private byte[] nonce; private byte[] initialAssociatedText; private byte[] H; @@ -90,14 +92,16 @@ namespace Org.BouncyCastle.Crypto.Modes { this.forEncryption = forEncryption; this.macBlock = null; + this.initialised = true; KeyParameter keyParam; + byte[] newNonce = null; if (parameters is AeadParameters) { AeadParameters param = (AeadParameters)parameters; - nonce = param.GetNonce(); + newNonce = param.GetNonce(); initialAssociatedText = param.GetAssociatedText(); int macSizeBits = param.MacSize; @@ -113,7 +117,7 @@ namespace Org.BouncyCastle.Crypto.Modes { ParametersWithIV param = (ParametersWithIV)parameters; - nonce = param.GetIV(); + newNonce = param.GetIV(); initialAssociatedText = null; macSize = 16; keyParam = (KeyParameter)param.Parameters; @@ -126,11 +130,32 @@ namespace Org.BouncyCastle.Crypto.Modes int bufLength = forEncryption ? BlockSize : (BlockSize + macSize); this.bufBlock = new byte[bufLength]; - if (nonce == null || nonce.Length < 1) + if (newNonce == null || newNonce.Length < 1) { throw new ArgumentException("IV must be at least 1 byte"); } + if (forEncryption) + { + if (nonce != null && Arrays.AreEqual(nonce, newNonce)) + { + if (keyParam == null) + { + throw new ArgumentException("cannot reuse nonce for GCM encryption"); + } + if (lastKey != null && Arrays.AreEqual(lastKey, keyParam.GetKey())) + { + throw new ArgumentException("cannot reuse nonce for GCM encryption"); + } + } + } + + nonce = newNonce; + if (keyParam != null) + { + lastKey = keyParam.GetKey(); + } + // TODO Restrict macSize to 16 if nonce length not 12? // Cipher always used in forward mode @@ -186,7 +211,9 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual byte[] GetMac() { - return Arrays.Clone(macBlock); + return macBlock == null + ? new byte[macSize] + : Arrays.Clone(macBlock); } public virtual int GetOutputSize( @@ -219,6 +246,8 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void ProcessAadByte(byte input) { + CheckStatus(); + atBlock[atBlockPos] = input; if (++atBlockPos == BlockSize) { @@ -231,6 +260,8 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) { + CheckStatus(); + for (int i = 0; i < len; ++i) { atBlock[atBlockPos] = inBytes[inOff + i]; @@ -270,6 +301,8 @@ namespace Org.BouncyCastle.Crypto.Modes byte[] output, int outOff) { + CheckStatus(); + bufBlock[bufOff] = input; if (++bufOff == bufBlock.Length) { @@ -286,6 +319,8 @@ namespace Org.BouncyCastle.Crypto.Modes byte[] output, int outOff) { + CheckStatus(); + if (input.Length < (inOff + len)) throw new DataLengthException("Input buffer too short"); @@ -325,6 +360,8 @@ namespace Org.BouncyCastle.Crypto.Modes public int DoFinal(byte[] output, int outOff) { + CheckStatus(); + if (totalLength == 0) { InitCipher(); @@ -441,6 +478,8 @@ namespace Org.BouncyCastle.Crypto.Modes { cipher.Reset(); + // note: we do not reset the nonce. + S = new byte[BlockSize]; S_at = new byte[BlockSize]; S_atPre = new byte[BlockSize]; @@ -463,9 +502,16 @@ namespace Org.BouncyCastle.Crypto.Modes macBlock = null; } - if (initialAssociatedText != null) + if (forEncryption) { - ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + initialised = false; + } + else + { + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } } } @@ -532,5 +578,17 @@ namespace Org.BouncyCastle.Crypto.Modes cipher.ProcessBlock(counter, 0, tmp, 0); return tmp; } + + private void CheckStatus() + { + if (!initialised) + { + if (forEncryption) + { + throw new InvalidOperationException("GCM cipher cannot be reused for encryption"); + } + throw new InvalidOperationException("GCM cipher needs to be initialised"); + } + } } } diff --git a/crypto/src/crypto/modes/GOFBBlockCipher.cs b/crypto/src/crypto/modes/GOFBBlockCipher.cs index 4299f11a9..436b58a1d 100644 --- a/crypto/src/crypto/modes/GOFBBlockCipher.cs +++ b/crypto/src/crypto/modes/GOFBBlockCipher.cs @@ -171,7 +171,10 @@ namespace Org.BouncyCastle.Crypto.Modes N4 += C1; if (N4 < C1) // addition is mod (2**32 - 1) { - N4++; + if (N4 > 0) + { + N4++; + } } intTobytes(N3, ofbV, 0); intTobytes(N4, ofbV, 4); diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs index e7dc466e6..91fdfff18 100644 --- a/crypto/src/crypto/modes/OCBBlockCipher.cs +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs @@ -237,7 +237,9 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual byte[] GetMac() { - return Arrays.Clone(macBlock); + return macBlock == null + ? new byte[macSize] + : Arrays.Clone(macBlock); } public virtual int GetOutputSize(int len) diff --git a/crypto/src/crypto/parameters/DHPublicKeyParameters.cs b/crypto/src/crypto/parameters/DHPublicKeyParameters.cs index e79375f71..e7aeeff19 100644 --- a/crypto/src/crypto/parameters/DHPublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/DHPublicKeyParameters.cs @@ -8,6 +8,25 @@ namespace Org.BouncyCastle.Crypto.Parameters public class DHPublicKeyParameters : DHKeyParameters { + private static BigInteger Validate(BigInteger y, DHParameters dhParams) + { + if (y == null) + throw new ArgumentNullException("y"); + + // TLS check + if (y.CompareTo(BigInteger.Two) < 0 || y.CompareTo(dhParams.P.Subtract(BigInteger.Two)) > 0) + throw new ArgumentException("invalid DH public key", "y"); + + // we can't validate without Q. + if (dhParams.Q != null + && !y.ModPow(dhParams.Q, dhParams.P).Equals(BigInteger.One)) + { + throw new ArgumentException("y value does not appear to be in correct group", "y"); + } + + return y; + } + private readonly BigInteger y; public DHPublicKeyParameters( @@ -15,10 +34,7 @@ namespace Org.BouncyCastle.Crypto.Parameters DHParameters parameters) : base(false, parameters) { - if (y == null) - throw new ArgumentNullException("y"); - - this.y = y; + this.y = Validate(y, parameters); } public DHPublicKeyParameters( @@ -27,13 +43,10 @@ namespace Org.BouncyCastle.Crypto.Parameters DerObjectIdentifier algorithmOid) : base(false, parameters, algorithmOid) { - if (y == null) - throw new ArgumentNullException("y"); - - this.y = y; + this.y = Validate(y, parameters); } - public BigInteger Y + public virtual BigInteger Y { get { return y; } } diff --git a/crypto/src/crypto/parameters/DsaPublicKeyParameters.cs b/crypto/src/crypto/parameters/DsaPublicKeyParameters.cs index f11f858f3..3a81bfdd0 100644 --- a/crypto/src/crypto/parameters/DsaPublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/DsaPublicKeyParameters.cs @@ -7,6 +7,22 @@ namespace Org.BouncyCastle.Crypto.Parameters public class DsaPublicKeyParameters : DsaKeyParameters { + private static BigInteger Validate(BigInteger y, DsaParameters parameters) + { + // we can't validate without params, fortunately we can't use the key either... + if (parameters != null) + { + if (y.CompareTo(BigInteger.Two) < 0 + || y.CompareTo(parameters.P.Subtract(BigInteger.Two)) > 0 + || !y.ModPow(parameters.Q, parameters.P).Equals(BigInteger.One)) + { + throw new ArgumentException("y value does not appear to be in correct group"); + } + } + + return y; + } + private readonly BigInteger y; public DsaPublicKeyParameters( @@ -17,7 +33,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (y == null) throw new ArgumentNullException("y"); - this.y = y; + this.y = Validate(y, parameters); } public BigInteger Y diff --git a/crypto/src/crypto/parameters/ECPublicKeyParameters.cs b/crypto/src/crypto/parameters/ECPublicKeyParameters.cs index 1eb665da9..474e5d82c 100644 --- a/crypto/src/crypto/parameters/ECPublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/ECPublicKeyParameters.cs @@ -9,6 +9,21 @@ namespace Org.BouncyCastle.Crypto.Parameters public class ECPublicKeyParameters : ECKeyParameters { + private static ECPoint Validate(ECPoint q) + { + if (q == null) + throw new ArgumentNullException("q"); + if (q.IsInfinity) + throw new ArgumentException("point at infinity", "q"); + + q = q.Normalize(); + + if (!q.IsValid()) + throw new ArgumentException("point not on curve", "q"); + + return q; + } + private readonly ECPoint q; public ECPublicKeyParameters( @@ -27,7 +42,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (q == null) throw new ArgumentNullException("q"); - this.q = q.Normalize(); + this.q = Validate(q); } public ECPublicKeyParameters( @@ -39,7 +54,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (q == null) throw new ArgumentNullException("q"); - this.q = q.Normalize(); + this.q = Validate(q); } public ECPublicKeyParameters( @@ -51,7 +66,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (q == null) throw new ArgumentNullException("q"); - this.q = q.Normalize(); + this.q = Validate(q); } public ECPoint Q diff --git a/crypto/src/crypto/parameters/RsaKeyParameters.cs b/crypto/src/crypto/parameters/RsaKeyParameters.cs index 72c0d806f..5480f0561 100644 --- a/crypto/src/crypto/parameters/RsaKeyParameters.cs +++ b/crypto/src/crypto/parameters/RsaKeyParameters.cs @@ -8,6 +8,26 @@ namespace Org.BouncyCastle.Crypto.Parameters public class RsaKeyParameters : AsymmetricKeyParameter { + // the value is the product of the 132 smallest primes from 3 to 751 + private static BigInteger SmallPrimesProduct = new BigInteger( + "8138E8A0FCF3A4E84A771D40FD305D7F4AA59306D7251DE54D98AF8FE95729A1" + + "F73D893FA424CD2EDC8636A6C3285E022B0E3866A565AE8108EED8591CD4FE8D" + + "2CE86165A978D719EBF647F362D33FCA29CD179FB42401CBAF3DF0C614056F9C" + + "8F3CFD51E474AFB6BC6974F78DB8ABA8E9E517FDED658591AB7502BD41849462F", + 16); + + private static BigInteger Validate(BigInteger modulus) + { + if ((modulus.IntValue & 1) == 0) + throw new ArgumentException("RSA modulus is even", "modulus"); + if (!modulus.Gcd(SmallPrimesProduct).Equals(BigInteger.One)) + throw new ArgumentException("RSA modulus has a small prime factor"); + + // TODO: add additional primePower/Composite test - expensive!! + + return modulus; + } + private readonly BigInteger modulus; private readonly BigInteger exponent; @@ -25,8 +45,10 @@ namespace Org.BouncyCastle.Crypto.Parameters throw new ArgumentException("Not a valid RSA modulus", "modulus"); if (exponent.SignValue <= 0) throw new ArgumentException("Not a valid RSA exponent", "exponent"); + if (!isPrivate && (exponent.IntValue & 1) == 0) + throw new ArgumentException("RSA publicExponent is even", "exponent"); - this.modulus = modulus; + this.modulus = Validate(modulus); this.exponent = exponent; } diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs index 5c5bda399..30c838c1b 100644 --- a/crypto/src/crypto/prng/SP800SecureRandom.cs +++ b/crypto/src/crypto/prng/SP800SecureRandom.cs @@ -76,5 +76,20 @@ namespace Org.BouncyCastle.Crypto.Prng { return EntropyUtilities.GenerateSeed(mEntropySource, numBytes); } + + /// <summary>Force a reseed of the DRBG.</summary> + /// <param name="additionalInput">optional additional input</param> + public virtual void Reseed(byte[] additionalInput) + { + lock (this) + { + if (mDrbg == null) + { + mDrbg = mDrbgProvider.Get(mEntropySource); + } + + mDrbg.Reseed(additionalInput); + } + } } } diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs index 3aa2e3719..6b8037095 100644 --- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs +++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs @@ -516,6 +516,7 @@ namespace Org.BouncyCastle.Crypto.Signers if (!isOkay) { fullMessage = false; + messageLength = 0; ClearBlock(recoveredMessage); return false; } @@ -528,12 +529,14 @@ namespace Org.BouncyCastle.Crypto.Signers { if (!IsSameAs(mBuf, recoveredMessage)) { + messageLength = 0; ClearBlock(mBuf); return false; } - messageLength = 0; } + messageLength = 0; + ClearBlock(mBuf); return true; } diff --git a/crypto/src/crypto/signers/Iso9796d2Signer.cs b/crypto/src/crypto/signers/Iso9796d2Signer.cs index b90ed8f0b..303913068 100644 --- a/crypto/src/crypto/signers/Iso9796d2Signer.cs +++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs @@ -360,6 +360,8 @@ namespace Org.BouncyCastle.Crypto.Signers byte[] b = cipher.ProcessBlock(block, 0, block.Length); + messageLength = 0; + ClearBlock(mBuf); ClearBlock(block); @@ -526,11 +528,15 @@ namespace Org.BouncyCastle.Crypto.Signers ClearBlock(mBuf); ClearBlock(block); + messageLength = 0; + return true; } private bool ReturnFalse(byte[] block) { + messageLength = 0; + ClearBlock(mBuf); ClearBlock(block); diff --git a/crypto/src/crypto/tls/EncryptionAlgorithm.cs b/crypto/src/crypto/tls/EncryptionAlgorithm.cs index 96037ed20..45eef18e3 100644 --- a/crypto/src/crypto/tls/EncryptionAlgorithm.cs +++ b/crypto/src/crypto/tls/EncryptionAlgorithm.cs @@ -56,10 +56,9 @@ namespace Org.BouncyCastle.Crypto.Tls public const int CAMELLIA_256_GCM = 20; /* - * draft-ietf-tls-chacha20-poly1305-04 + * RFC 7905 */ - public const int CHACHA20_POLY1305 = 102; - [Obsolete] public const int AEAD_CHACHA20_POLY1305 = CHACHA20_POLY1305; + public const int CHACHA20_POLY1305 = 21; /* * draft-zauner-tls-aes-ocb-04 diff --git a/crypto/src/crypto/tls/ExtensionType.cs b/crypto/src/crypto/tls/ExtensionType.cs index bff9332a0..f17210b80 100644 --- a/crypto/src/crypto/tls/ExtensionType.cs +++ b/crypto/src/crypto/tls/ExtensionType.cs @@ -99,6 +99,16 @@ namespace Org.BouncyCastle.Crypto.Tls public const int extended_master_secret = 23; /* + * draft-ietf-tokbind-negotiation-08 + */ + public static readonly int DRAFT_token_binding = 24; + + /* + * RFC 7924 + */ + public const int cached_info = 25; + + /* * RFC 5077 7. */ public const int session_ticket = 35; diff --git a/crypto/src/crypto/tls/RecordStream.cs b/crypto/src/crypto/tls/RecordStream.cs index 46673cf7e..5d556ad06 100644 --- a/crypto/src/crypto/tls/RecordStream.cs +++ b/crypto/src/crypto/tls/RecordStream.cs @@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Tls 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 SequenceNumber mReadSeqNo = new SequenceNumber(), mWriteSeqNo = new SequenceNumber(); private MemoryStream mBuffer = new MemoryStream(); private TlsHandshakeHash mHandshakeHash = null; @@ -100,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Tls this.mWriteCompression = this.mPendingCompression; this.mWriteCipher = this.mPendingCipher; - this.mWriteSeqNo = 0; + this.mWriteSeqNo = new SequenceNumber(); } internal virtual void ReceivedReadCipherSpec() @@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Tls this.mReadCompression = this.mPendingCompression; this.mReadCipher = this.mPendingCipher; - this.mReadSeqNo = 0; + this.mReadSeqNo = new SequenceNumber(); } internal virtual void FinaliseHandshake() @@ -203,7 +203,9 @@ namespace Org.BouncyCastle.Crypto.Tls internal virtual byte[] DecodeAndVerify(byte type, Stream input, int len) { byte[] buf = TlsUtilities.ReadFully(len, input); - byte[] decoded = mReadCipher.DecodeCiphertext(mReadSeqNo++, type, buf, 0, buf.Length); + + long seqNo = mReadSeqNo.NextValue(AlertDescription.unexpected_message); + byte[] decoded = mReadCipher.DecodeCiphertext(seqNo, type, buf, 0, buf.Length); CheckLength(decoded.Length, mCompressedLimit, AlertDescription.record_overflow); @@ -262,10 +264,12 @@ namespace Org.BouncyCastle.Crypto.Tls Stream cOut = mWriteCompression.Compress(mBuffer); + long seqNo = mWriteSeqNo.NextValue(AlertDescription.internal_error); + byte[] ciphertext; if (cOut == mBuffer) { - ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, plaintext, plaintextOffset, plaintextLength); + ciphertext = mWriteCipher.EncodePlaintext(seqNo, type, plaintext, plaintextOffset, plaintextLength); } else { @@ -279,7 +283,7 @@ namespace Org.BouncyCastle.Crypto.Tls */ CheckLength(compressed.Length, plaintextLength + 1024, AlertDescription.internal_error); - ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, compressed, 0, compressed.Length); + ciphertext = mWriteCipher.EncodePlaintext(seqNo, type, compressed, 0, compressed.Length); } /* @@ -384,5 +388,25 @@ namespace Org.BouncyCastle.Crypto.Tls mOuter.mHandshakeHash.BlockUpdate(buf, off, len); } } + + private class SequenceNumber + { + private long value = 0L; + private bool exhausted = false; + + internal long NextValue(byte alertDescription) + { + if (exhausted) + { + throw new TlsFatalAlert(alertDescription); + } + long result = value; + if (++value == 0) + { + exhausted = true; + } + return result; + } + } } } diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs index eec9daaca..d179068bb 100644 --- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs @@ -151,6 +151,9 @@ namespace Org.BouncyCastle.Crypto.Tls public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { + if (mKeyExchange == KeyExchangeAlgorithm.DH_anon) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + byte[] types = certificateRequest.CertificateTypes; for (int i = 0; i < types.Length; ++i) { diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs index 03c162904..c508fb993 100644 --- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs @@ -153,6 +153,9 @@ namespace Org.BouncyCastle.Crypto.Tls public override void ValidateCertificateRequest(CertificateRequest certificateRequest) { + if (mKeyExchange == KeyExchangeAlgorithm.ECDH_anon) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + /* * 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 diff --git a/crypto/src/crypto/tls/TlsException.cs b/crypto/src/crypto/tls/TlsException.cs new file mode 100644 index 000000000..cea9e3e77 --- /dev/null +++ b/crypto/src/crypto/tls/TlsException.cs @@ -0,0 +1,14 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsException + : IOException + { + public TlsException(string message, Exception cause) + : base(message, cause) + { + } + } +} diff --git a/crypto/src/crypto/tls/TlsFatalAlert.cs b/crypto/src/crypto/tls/TlsFatalAlert.cs index 55d784dd9..6f1898179 100644 --- a/crypto/src/crypto/tls/TlsFatalAlert.cs +++ b/crypto/src/crypto/tls/TlsFatalAlert.cs @@ -1,10 +1,9 @@ using System; -using System.IO; namespace Org.BouncyCastle.Crypto.Tls { public class TlsFatalAlert - : IOException + : TlsException { private readonly byte alertDescription; diff --git a/crypto/src/crypto/tls/TlsFatalAlertReceived.cs b/crypto/src/crypto/tls/TlsFatalAlertReceived.cs new file mode 100644 index 000000000..044fc8027 --- /dev/null +++ b/crypto/src/crypto/tls/TlsFatalAlertReceived.cs @@ -0,0 +1,21 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsFatalAlertReceived + : TlsException + { + private readonly byte alertDescription; + + public TlsFatalAlertReceived(byte alertDescription) + : base(Tls.AlertDescription.GetText(alertDescription), null) + { + this.alertDescription = alertDescription; + } + + public virtual byte AlertDescription + { + get { return alertDescription; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsNoCloseNotifyException.cs b/crypto/src/crypto/tls/TlsNoCloseNotifyException.cs index 72159ba47..0bafd820b 100644 --- a/crypto/src/crypto/tls/TlsNoCloseNotifyException.cs +++ b/crypto/src/crypto/tls/TlsNoCloseNotifyException.cs @@ -15,5 +15,9 @@ namespace Org.BouncyCastle.Crypto.Tls public class TlsNoCloseNotifyException : EndOfStreamException { + public TlsNoCloseNotifyException() + : base("No close_notify alert received before connection closed") + { + } } } diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs index 5a1c08616..72151d414 100644 --- a/crypto/src/crypto/tls/TlsProtocol.cs +++ b/crypto/src/crypto/tls/TlsProtocol.cs @@ -108,16 +108,95 @@ namespace Org.BouncyCastle.Crypto.Tls protected abstract TlsPeer Peer { get; } + protected virtual void HandleAlertMessage(byte alertLevel, byte alertDescription) + { + Peer.NotifyAlertReceived(alertLevel, alertDescription); + + if (alertLevel == AlertLevel.warning) + { + HandleAlertWarningMessage(alertDescription); + } + else + { + HandleFailure(); + + throw new TlsFatalAlertReceived(alertDescription); + } + } + + protected virtual void HandleAlertWarningMessage(byte alertDescription) + { + /* + * 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. + */ + if (alertDescription == AlertDescription.close_notify) + { + if (!mAppDataReady) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + HandleClose(false); + } + } + protected virtual void HandleChangeCipherSpecMessage() { } - protected abstract void HandleHandshakeMessage(byte type, MemoryStream buf); + protected virtual void HandleClose(bool user_canceled) + { + if (!mClosed) + { + this.mClosed = true; + + if (user_canceled && !mAppDataReady) + { + RaiseAlertWarning(AlertDescription.user_canceled, "User canceled handshake"); + } + + RaiseAlertWarning(AlertDescription.close_notify, "Connection closed"); + + mRecordStream.SafeClose(); + + if (!mAppDataReady) + { + CleanupHandshake(); + } + } + } + + protected virtual void HandleException(byte alertDescription, string message, Exception cause) + { + if (!mClosed) + { + RaiseAlertFatal(alertDescription, message, cause); + + HandleFailure(); + } + } - protected virtual void HandleWarningMessage(byte description) + protected virtual void HandleFailure() { + this.mClosed = true; + this.mFailedWithError = true; + + /* + * 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(); + + mRecordStream.SafeClose(); + + if (!mAppDataReady) + { + CleanupHandshake(); + } } + protected abstract void HandleHandshakeMessage(byte type, MemoryStream buf); + protected virtual void ApplyMaxFragmentLengthExtension() { if (mSecurityParameters.maxFragmentLength >= 0) @@ -366,51 +445,11 @@ namespace Org.BouncyCastle.Crypto.Tls /* * 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(); - if (!mAppDataReady) - { - CleanupHandshake(); - } + byte[] alert = mAlertQueue.RemoveData(2, 0); + byte alertLevel = alert[0]; + byte alertDescription = alert[1]; - throw new IOException("Fatal alert received from TLS peer: " + AlertDescription.GetText(description)); - } - 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. - */ - if (description == AlertDescription.close_notify) - { - if (!mAppDataReady) - { - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - HandleClose(false); - } - - /* - * If it is just a warning, we continue. - */ - HandleWarningMessage(description); - } + HandleAlertMessage(alertLevel, alertDescription); } } @@ -493,45 +532,55 @@ namespace Org.BouncyCastle.Crypto.Tls } catch (TlsFatalAlert e) { - this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e); + HandleException(e.AlertDescription, "Failed to read record", e); throw e; } - catch (Exception e) + catch (IOException e) { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); + HandleException(AlertDescription.internal_error, "Failed to read record", e); throw e; } + catch (Exception e) + { + HandleException(AlertDescription.internal_error, "Failed to read record", e); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } } protected virtual void SafeReadRecord() { try { - if (!mRecordStream.ReadRecord()) - { - if (!mAppDataReady) - { - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - throw new TlsNoCloseNotifyException(); - } + if (mRecordStream.ReadRecord()) + return; + + if (!mAppDataReady) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + catch (TlsFatalAlertReceived e) + { + // Connection failure already handled at source + throw e; } catch (TlsFatalAlert e) { - if (!mClosed) - { - this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e); - } + HandleException(e.AlertDescription, "Failed to read record", e); throw e; } - catch (Exception e) + catch (IOException e) { - if (!mClosed) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); - } + HandleException(AlertDescription.internal_error, "Failed to read record", e); throw e; } + catch (Exception e) + { + HandleException(AlertDescription.internal_error, "Failed to read record", e); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + + HandleFailure(); + + throw new TlsNoCloseNotifyException(); } protected virtual void SafeWriteRecord(byte type, byte[] buf, int offset, int len) @@ -542,20 +591,19 @@ namespace Org.BouncyCastle.Crypto.Tls } catch (TlsFatalAlert e) { - if (!mClosed) - { - this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to write record", e); - } + HandleException(e.AlertDescription, "Failed to write record", e); throw e; } - catch (Exception e) + catch (IOException e) { - if (!mClosed) - { - this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e); - } + HandleException(AlertDescription.internal_error, "Failed to write record", e); throw e; } + catch (Exception e) + { + HandleException(AlertDescription.internal_error, "Failed to write record", e); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } } /** @@ -832,50 +880,6 @@ namespace Org.BouncyCastle.Crypto.Tls return mOutputBuffer.Read(buffer, offset, length); } - /** - * 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 connection failed"); - } - protected virtual void InvalidateSession() { if (this.mSessionParameters != null) @@ -912,18 +916,29 @@ namespace Org.BouncyCastle.Crypto.Tls } } - protected virtual void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause) + protected virtual void RaiseAlertFatal(byte alertDescription, string message, Exception cause) { - Peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause); + Peer.NotifyAlertRaised(AlertLevel.fatal, alertDescription, message, cause); - byte[] error = new byte[]{ alertLevel, alertDescription }; + byte[] alert = new byte[]{ AlertLevel.fatal, alertDescription }; - SafeWriteRecord(ContentType.alert, error, 0, 2); + try + { + mRecordStream.WriteRecord(ContentType.alert, alert, 0, 2); + } + catch (Exception) + { + // We are already processing an exception, so just ignore this + } } - protected virtual void RaiseWarning(byte alertDescription, string message) + protected virtual void RaiseAlertWarning(byte alertDescription, string message) { - RaiseAlert(AlertLevel.warning, alertDescription, message, null); + Peer.NotifyAlertRaised(AlertLevel.warning, alertDescription, message, null); + + byte[] alert = new byte[]{ AlertLevel.warning, alertDescription }; + + SafeWriteRecord(ContentType.alert, alert, 0, 2); } protected virtual void SendCertificateMessage(Certificate certificate) @@ -942,7 +957,7 @@ namespace Org.BouncyCastle.Crypto.Tls if (serverVersion.IsSsl) { string errorMessage = serverVersion.ToString() + " client didn't provide credentials"; - RaiseWarning(AlertDescription.no_certificate, errorMessage); + RaiseAlertWarning(AlertDescription.no_certificate, errorMessage); return; } } @@ -1001,18 +1016,6 @@ namespace Org.BouncyCastle.Crypto.Tls 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(); @@ -1048,7 +1051,7 @@ namespace Org.BouncyCastle.Crypto.Tls if (TlsUtilities.IsSsl(Context)) throw new TlsFatalAlert(AlertDescription.handshake_failure); - RaiseWarning(AlertDescription.no_renegotiation, "Renegotiation not supported"); + RaiseAlertWarning(AlertDescription.no_renegotiation, "Renegotiation not supported"); } /** diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs index 298c9f42d..c2bfbcb74 100644 --- a/crypto/src/crypto/tls/TlsServerProtocol.cs +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs @@ -386,11 +386,11 @@ namespace Org.BouncyCastle.Crypto.Tls } } - protected override void HandleWarningMessage(byte description) + protected override void HandleAlertWarningMessage(byte alertDescription) { - base.HandleWarningMessage(description); + base.HandleAlertWarningMessage(alertDescription); - switch (description) + switch (alertDescription) { case AlertDescription.no_certificate: { diff --git a/crypto/src/math/ec/custom/gm/SM2P256V1FieldElement.cs b/crypto/src/math/ec/custom/gm/SM2P256V1FieldElement.cs index 669c73bd2..4f6428f9e 100644 --- a/crypto/src/math/ec/custom/gm/SM2P256V1FieldElement.cs +++ b/crypto/src/math/ec/custom/gm/SM2P256V1FieldElement.cs @@ -143,13 +143,13 @@ namespace Org.BouncyCastle.Math.EC.Custom.GM uint[] x2 = Nat256.Create(); SM2P256V1Field.Square(x1, x2); SM2P256V1Field.Multiply(x2, x1, x2); - uint[] x3 = x2; - SM2P256V1Field.Square(x2, x3); - SM2P256V1Field.Multiply(x3, x1, x3); + uint[] x4 = Nat256.Create(); + SM2P256V1Field.SquareN(x2, 2, x4); + SM2P256V1Field.Multiply(x4, x2, x4); uint[] x6 = Nat256.Create(); - SM2P256V1Field.SquareN(x3, 3, x6); - SM2P256V1Field.Multiply(x6, x3, x6); - uint[] x12 = x3; + SM2P256V1Field.SquareN(x4, 2, x6); + SM2P256V1Field.Multiply(x6, x2, x6); + uint[] x12 = x2; SM2P256V1Field.SquareN(x6, 6, x12); SM2P256V1Field.Multiply(x12, x6, x12); uint[] x24 = Nat256.Create(); @@ -162,25 +162,23 @@ namespace Org.BouncyCastle.Math.EC.Custom.GM SM2P256V1Field.Square(x30, x31); SM2P256V1Field.Multiply(x31, x1, x31); - uint[] t1 = x31; - SM2P256V1Field.Square(x31, t1); + uint[] t1 = x24; + SM2P256V1Field.SquareN(x31, 31, t1); - uint[] x32 = x12; - SM2P256V1Field.Multiply(t1, x1, x32); + uint[] x62 = x30; + SM2P256V1Field.Multiply(t1, x31, x62); SM2P256V1Field.SquareN(t1, 32, t1); - SM2P256V1Field.Multiply(t1, x32, t1); - - uint[] t2 = x24; - SM2P256V1Field.SquareN(t1, 32, t2); - SM2P256V1Field.Multiply(t2, x1, t2); - SM2P256V1Field.SquareN(t2, 32, t2); - SM2P256V1Field.Multiply(t2, t1, t2); - SM2P256V1Field.SquareN(t2, 32, t2); - SM2P256V1Field.Multiply(t2, x32, t2); - SM2P256V1Field.SquareN(t2, 32, t2); - SM2P256V1Field.Multiply(t2, x1, t2); - SM2P256V1Field.SquareN(t2, 62, t1); + SM2P256V1Field.Multiply(t1, x62, t1); + SM2P256V1Field.SquareN(t1, 62, t1); + SM2P256V1Field.Multiply(t1, x62, t1); + SM2P256V1Field.SquareN(t1, 4, t1); + SM2P256V1Field.Multiply(t1, x4, t1); + SM2P256V1Field.SquareN(t1, 32, t1); + SM2P256V1Field.Multiply(t1, x1, t1); + SM2P256V1Field.SquareN(t1, 62, t1); + + uint[] t2 = x4; SM2P256V1Field.Square(t1, t2); return Nat256.Eq(x1, t2) ? new SM2P256V1FieldElement(t1) : null; diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs index 055f99636..7d96dee8d 100644 --- a/crypto/src/openpgp/PgpUtilities.cs +++ b/crypto/src/openpgp/PgpUtilities.cs @@ -417,7 +417,20 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return inputStream; } - else + + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + byte[] buf = new byte[ReadAhead]; + int count = 1; + int index = 1; + + buf[0] = (byte)ch; + while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0) { if (!IsPossiblyBase64(ch)) { @@ -426,51 +439,49 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return new ArmoredInputStream(inputStream); } - byte[] buf = new byte[ReadAhead]; - int count = 1; - int index = 1; - - buf[0] = (byte)ch; - while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0) + if (ch != '\n' && ch != '\r') { - if (!IsPossiblyBase64(ch)) - { - inputStream.Position = markedPos; + buf[index++] = (byte)ch; + } - return new ArmoredInputStream(inputStream); - } + count++; + } - if (ch != '\n' && ch != '\r') - { - buf[index++] = (byte)ch; - } + inputStream.Position = markedPos; - count++; - } + // + // nothing but new lines, little else, assume regular armoring + // + if (count < 4) + { + return new ArmoredInputStream(inputStream); + } - inputStream.Position = markedPos; + // + // test our non-blank data + // + byte[] firstBlock = new byte[8]; - // - // nothing but new lines, little else, assume regular armoring - // - if (count < 4) - { - return new ArmoredInputStream(inputStream); - } + Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length); - // - // test our non-blank data - // - byte[] firstBlock = new byte[8]; - Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length); - byte[] decoded = Base64.Decode(firstBlock); + try + { + byte[] decoded = Base64.Decode(firstBlock); - // + // // it's a base64 PGP block. // - bool hasHeaders = (decoded[0] & 0x80) == 0; + bool hasHeaders = (decoded[0] & 0x80) == 0; - return new ArmoredInputStream(inputStream, hasHeaders); + return new ArmoredInputStream(inputStream, hasHeaders); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new IOException(e.Message); } } diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs index 3217f3183..de05bc9ef 100644 --- a/crypto/src/security/CipherUtilities.cs +++ b/crypto/src/security/CipherUtilities.cs @@ -323,7 +323,7 @@ namespace Org.BouncyCastle.Security "PBEWITHMD5AND256BITAES-CBC-OPENSSL")) { return new PaddedBufferedBlockCipher( - new CbcBlockCipher(new AesFastEngine())); + new CbcBlockCipher(new AesEngine())); } } } @@ -358,7 +358,7 @@ namespace Org.BouncyCastle.Security switch (cipherAlgorithm) { case CipherAlgorithm.AES: - blockCipher = new AesFastEngine(); + blockCipher = new AesEngine(); break; case CipherAlgorithm.ARC4: streamCipher = new RC4Engine(); @@ -722,7 +722,7 @@ namespace Org.BouncyCastle.Security { switch (cipherAlgorithm) { - case CipherAlgorithm.AES: return new AesFastEngine(); + case CipherAlgorithm.AES: return new AesEngine(); case CipherAlgorithm.BLOWFISH: return new BlowfishEngine(); case CipherAlgorithm.CAMELLIA: return new CamelliaEngine(); case CipherAlgorithm.CAST5: return new Cast5Engine(); diff --git a/crypto/src/security/MacUtilities.cs b/crypto/src/security/MacUtilities.cs index fab9b1d41..278f3bec1 100644 --- a/crypto/src/security/MacUtilities.cs +++ b/crypto/src/security/MacUtilities.cs @@ -136,7 +136,7 @@ namespace Org.BouncyCastle.Security if (mechanism == "AESCMAC") { - return new CMac(new AesFastEngine()); + return new CMac(new AesEngine()); } if (mechanism == "DESMAC") { diff --git a/crypto/src/security/WrapperUtilities.cs b/crypto/src/security/WrapperUtilities.cs index ce31ea519..c57632081 100644 --- a/crypto/src/security/WrapperUtilities.cs +++ b/crypto/src/security/WrapperUtilities.cs @@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Security case WrapAlgorithm.RC2WRAP: return new RC2WrapEngine(); case WrapAlgorithm.SEEDWRAP: return new SeedWrapEngine(); case WrapAlgorithm.DESEDERFC3211WRAP: return new Rfc3211WrapEngine(new DesEdeEngine()); - case WrapAlgorithm.AESRFC3211WRAP: return new Rfc3211WrapEngine(new AesFastEngine()); + case WrapAlgorithm.AESRFC3211WRAP: return new Rfc3211WrapEngine(new AesEngine()); case WrapAlgorithm.CAMELLIARFC3211WRAP: return new Rfc3211WrapEngine(new CamelliaEngine()); } } |