diff options
Diffstat (limited to 'crypto/src')
60 files changed, 2618 insertions, 504 deletions
diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs index 52467fabe..a2d57bf9d 100644 --- a/crypto/src/asn1/DerApplicationSpecific.cs +++ b/crypto/src/asn1/DerApplicationSpecific.cs @@ -199,38 +199,27 @@ namespace Org.BouncyCastle.Asn1 { int tagNo = input[0] & 0x1f; int index = 1; - // - // with tagged object tag number is bottom 5 bits, or stored at the start of the content - // + + // with tagged object tag number is bottom 5 bits, or stored at the start of the content if (tagNo == 0x1f) { - tagNo = 0; - - int b = input[index++] & 0xff; + int b = input[index++]; - // X.690-0207 8.1.2.4.2 + // X.690-0207 8.1.2.4.2 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." if ((b & 0x7f) == 0) // Note: -1 will pass - { - throw new InvalidOperationException("corrupted stream - invalid high tag number found"); - } + throw new IOException("corrupted stream - invalid high tag number found"); - while ((b >= 0) && ((b & 0x80) != 0)) + while ((b & 0x80) != 0) { - tagNo |= (b & 0x7f); - tagNo <<= 7; - b = input[index++] & 0xff; + b = input[index++]; } - - tagNo |= (b & 0x7f); } - byte[] tmp = new byte[input.Length - index + 1]; - - Array.Copy(input, index, tmp, 1, tmp.Length - 1); - - tmp[0] = (byte)newTag; - + int remaining = input.Length - index; + byte[] tmp = new byte[1 + remaining]; + tmp[0] = (byte)newTag; + Array.Copy(input, index, tmp, 1, remaining); return tmp; } } diff --git a/crypto/src/asn1/edec/EdECObjectIdentifiers.cs b/crypto/src/asn1/edec/EdECObjectIdentifiers.cs new file mode 100644 index 000000000..f8c5713d8 --- /dev/null +++ b/crypto/src/asn1/edec/EdECObjectIdentifiers.cs @@ -0,0 +1,17 @@ +using System; + +namespace Org.BouncyCastle.Asn1.EdEC +{ + /** + * Edwards Elliptic Curve Object Identifiers (RFC 8410) + */ + public abstract class EdECObjectIdentifiers + { + public static readonly DerObjectIdentifier id_edwards_curve_algs = new DerObjectIdentifier("1.3.101"); + + public static readonly DerObjectIdentifier id_X25519 = id_edwards_curve_algs.Branch("110"); + public static readonly DerObjectIdentifier id_X448 = id_edwards_curve_algs.Branch("111"); + public static readonly DerObjectIdentifier id_Ed25519 = id_edwards_curve_algs.Branch("112"); + public static readonly DerObjectIdentifier id_Ed448 = id_edwards_curve_algs.Branch("113"); + } +} diff --git a/crypto/src/asn1/pkcs/EncryptionScheme.cs b/crypto/src/asn1/pkcs/EncryptionScheme.cs index 7b90ece53..34d26e172 100644 --- a/crypto/src/asn1/pkcs/EncryptionScheme.cs +++ b/crypto/src/asn1/pkcs/EncryptionScheme.cs @@ -8,7 +8,13 @@ namespace Org.BouncyCastle.Asn1.Pkcs public class EncryptionScheme : AlgorithmIdentifier { - public EncryptionScheme( + public EncryptionScheme( + DerObjectIdentifier objectID) + : base(objectID) + { + } + + public EncryptionScheme( DerObjectIdentifier objectID, Asn1Encodable parameters) : base(objectID, parameters) diff --git a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs index c5be7a315..dfb332fdd 100644 --- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs +++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs @@ -4,15 +4,55 @@ using System.IO; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Asn1.Pkcs { + /** + * RFC 5958 + * + * <pre> + * [IMPLICIT TAGS] + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * PrivateKeyInfo ::= OneAsymmetricKey + * + * Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) + * + * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * { PUBLIC-KEY, + * { PrivateKeyAlgorithms } } + * + * PrivateKey ::= OCTET STRING + * -- Content varies based on type of key. The + * -- algorithm identifier dictates the format of + * -- the key. + * + * PublicKey ::= BIT STRING + * -- Content varies based on type of key. The + * -- algorithm identifier dictates the format of + * -- the key. + * + * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } + * </pre> + */ public class PrivateKeyInfo : Asn1Encodable { - private readonly Asn1OctetString privKey; - private readonly AlgorithmIdentifier algID; - private readonly Asn1Set attributes; + private readonly DerInteger version; + private readonly AlgorithmIdentifier privateKeyAlgorithm; + private readonly Asn1OctetString privateKey; + private readonly Asn1Set attributes; + private readonly DerBitString publicKey; public static PrivateKeyInfo GetInstance(Asn1TaggedObject obj, bool explicitly) { @@ -25,110 +65,139 @@ namespace Org.BouncyCastle.Asn1.Pkcs if (obj == null) return null; if (obj is PrivateKeyInfo) - return (PrivateKeyInfo) obj; + return (PrivateKeyInfo)obj; return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); } - public PrivateKeyInfo(AlgorithmIdentifier algID, Asn1Encodable privateKey) - : this(algID, privateKey, null) + private static int GetVersionValue(DerInteger version) + { + BigInteger bigValue = version.Value; + if (bigValue.CompareTo(BigInteger.Zero) < 0 || bigValue.CompareTo(BigInteger.One) > 0) + throw new ArgumentException("invalid version for private key info", "version"); + + return bigValue.IntValue; + } + + public PrivateKeyInfo( + AlgorithmIdentifier privateKeyAlgorithm, + Asn1Encodable privateKey) + : this(privateKeyAlgorithm, privateKey, null, null) { } public PrivateKeyInfo( - AlgorithmIdentifier algID, - Asn1Encodable privateKey, - Asn1Set attributes) + AlgorithmIdentifier privateKeyAlgorithm, + Asn1Encodable privateKey, + Asn1Set attributes) + : this(privateKeyAlgorithm, privateKey, attributes, null) { - this.algID = algID; - this.privKey = new DerOctetString(privateKey.GetEncoded(Asn1Encodable.Der)); + } + + public PrivateKeyInfo( + AlgorithmIdentifier privateKeyAlgorithm, + Asn1Encodable privateKey, + Asn1Set attributes, + byte[] publicKey) + { + this.version = new DerInteger(publicKey != null ? BigInteger.One : BigInteger.Zero); + this.privateKeyAlgorithm = privateKeyAlgorithm; + this.privateKey = new DerOctetString(privateKey); this.attributes = attributes; + this.publicKey = publicKey == null ? null : new DerBitString(publicKey); } private PrivateKeyInfo(Asn1Sequence seq) { IEnumerator e = seq.GetEnumerator(); - e.MoveNext(); - BigInteger version = ((DerInteger)e.Current).Value; - if (version.IntValue != 0) - { - throw new ArgumentException("wrong version for private key info: " + version.IntValue); - } + this.version = DerInteger.GetInstance(CollectionUtilities.RequireNext(e)); - e.MoveNext(); - algID = AlgorithmIdentifier.GetInstance(e.Current); - e.MoveNext(); - privKey = Asn1OctetString.GetInstance(e.Current); + int versionValue = GetVersionValue(version); - if (e.MoveNext()) + this.privateKeyAlgorithm = AlgorithmIdentifier.GetInstance(CollectionUtilities.RequireNext(e)); + this.privateKey = Asn1OctetString.GetInstance(CollectionUtilities.RequireNext(e)); + + int lastTag = -1; + while (e.MoveNext()) { - attributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false); + Asn1TaggedObject tagged = (Asn1TaggedObject)e.Current; + + int tag = tagged.TagNo; + if (tag <= lastTag) + throw new ArgumentException("invalid optional field in private key info", "seq"); + + lastTag = tag; + + switch (tag) + { + case 0: + { + this.attributes = Asn1Set.GetInstance(tagged, false); + break; + } + case 1: + { + if (versionValue < 1) + throw new ArgumentException("'publicKey' requires version v2(1) or later", "seq"); + + this.publicKey = DerBitString.GetInstance(tagged, false); + break; + } + default: + { + throw new ArgumentException("unknown optional field in private key info", "seq"); + } + } } } - public virtual AlgorithmIdentifier PrivateKeyAlgorithm + public virtual Asn1Set Attributes { - get { return algID; } + get { return attributes; } } - [Obsolete("Use 'PrivateKeyAlgorithm' property instead")] - public virtual AlgorithmIdentifier AlgorithmID + /// <summary>Return true if a public key is present, false otherwise.</summary> + public virtual bool HasPublicKey { - get { return algID; } + get { return publicKey != null; } + } + + public virtual AlgorithmIdentifier PrivateKeyAlgorithm + { + get { return privateKeyAlgorithm; } } public virtual Asn1Object ParsePrivateKey() { - return Asn1Object.FromByteArray(privKey.GetOctets()); + return Asn1Object.FromByteArray(privateKey.GetOctets()); } - [Obsolete("Use 'ParsePrivateKey' instead")] - public virtual Asn1Object PrivateKey + /// <summary>For when the public key is an ASN.1 encoding.</summary> + public virtual Asn1Object ParsePublicKey() { - get - { - try - { - return ParsePrivateKey(); - } - catch (IOException) - { - throw new InvalidOperationException("unable to parse private key"); - } - } + return publicKey == null ? null : Asn1Object.FromByteArray(publicKey.GetOctets()); } - public virtual Asn1Set Attributes + /// <summary>Return the public key as a raw bit string.</summary> + public virtual DerBitString PublicKeyData { - get { return attributes; } + get { return publicKey; } } - /** - * write out an RSA private key with its associated information - * as described in Pkcs8. - * <pre> - * PrivateKeyInfo ::= Sequence { - * version Version, - * privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, - * privateKey PrivateKey, - * attributes [0] IMPLICIT Attributes OPTIONAL - * } - * Version ::= Integer {v1(0)} (v1,...) - * - * PrivateKey ::= OCTET STRING - * - * Attributes ::= Set OF Attr - * </pre> - */ public override Asn1Object ToAsn1Object() { - Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(0), algID, privKey); + Asn1EncodableVector v = new Asn1EncodableVector(version, privateKeyAlgorithm, privateKey); if (attributes != null) { v.Add(new DerTaggedObject(false, 0, attributes)); } + if (publicKey != null) + { + v.Add(new DerTaggedObject(false, 1, publicKey)); + } + return new DerSequence(v); } } diff --git a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs index 721299105..738a97eb4 100644 --- a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs +++ b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs @@ -9,14 +9,14 @@ namespace Org.BouncyCastle.Asn1.Pkcs public class RsaPrivateKeyStructure : Asn1Encodable { - private readonly BigInteger modulus; - private readonly BigInteger publicExponent; - private readonly BigInteger privateExponent; - private readonly BigInteger prime1; - private readonly BigInteger prime2; - private readonly BigInteger exponent1; - private readonly BigInteger exponent2; - private readonly BigInteger coefficient; + private readonly BigInteger modulus; + private readonly BigInteger publicExponent; + private readonly BigInteger privateExponent; + private readonly BigInteger prime1; + private readonly BigInteger prime2; + private readonly BigInteger exponent1; + private readonly BigInteger exponent2; + private readonly BigInteger coefficient; public static RsaPrivateKeyStructure GetInstance(Asn1TaggedObject obj, bool isExplicit) { @@ -33,14 +33,14 @@ namespace Org.BouncyCastle.Asn1.Pkcs } public RsaPrivateKeyStructure( - BigInteger modulus, - BigInteger publicExponent, - BigInteger privateExponent, - BigInteger prime1, - BigInteger prime2, - BigInteger exponent1, - BigInteger exponent2, - BigInteger coefficient) + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger prime1, + BigInteger prime2, + BigInteger exponent1, + BigInteger exponent2, + BigInteger coefficient) { this.modulus = modulus; this.publicExponent = publicExponent; @@ -56,18 +56,18 @@ namespace Org.BouncyCastle.Asn1.Pkcs public RsaPrivateKeyStructure( Asn1Sequence seq) { - BigInteger version = ((DerInteger) seq[0]).Value; + BigInteger version = ((DerInteger)seq[0]).Value; if (version.IntValue != 0) throw new ArgumentException("wrong version for RSA private key"); - modulus = ((DerInteger) seq[1]).Value; - publicExponent = ((DerInteger) seq[2]).Value; - privateExponent = ((DerInteger) seq[3]).Value; - prime1 = ((DerInteger) seq[4]).Value; - prime2 = ((DerInteger) seq[5]).Value; - exponent1 = ((DerInteger) seq[6]).Value; - exponent2 = ((DerInteger) seq[7]).Value; - coefficient = ((DerInteger) seq[8]).Value; + modulus = ((DerInteger)seq[1]).Value; + publicExponent = ((DerInteger)seq[2]).Value; + privateExponent = ((DerInteger)seq[3]).Value; + prime1 = ((DerInteger)seq[4]).Value; + prime2 = ((DerInteger)seq[5]).Value; + exponent1 = ((DerInteger)seq[6]).Value; + exponent2 = ((DerInteger)seq[7]).Value; + coefficient = ((DerInteger)seq[8]).Value; } public BigInteger Modulus diff --git a/crypto/src/asn1/x509/TBSCertificateStructure.cs b/crypto/src/asn1/x509/TBSCertificateStructure.cs index 9df078539..e69e985f5 100644 --- a/crypto/src/asn1/x509/TBSCertificateStructure.cs +++ b/crypto/src/asn1/x509/TBSCertificateStructure.cs @@ -121,7 +121,7 @@ namespace Org.BouncyCastle.Asn1.X509 while (extras > 0) { - DerTaggedObject extra = (DerTaggedObject) seq[seqStart + 6 + extras]; + DerTaggedObject extra = (DerTaggedObject)seq[seqStart + 6 + extras]; switch (extra.TagNo) { @@ -140,9 +140,13 @@ namespace Org.BouncyCastle.Asn1.X509 if (isV2) throw new ArgumentException("version 2 certificate cannot contain extensions"); - extensions = X509Extensions.GetInstance(extra); + extensions = X509Extensions.GetInstance(Asn1Sequence.GetInstance(extra, true)); break; } + default: + { + throw new ArgumentException("Unknown tag encountered in structure: " + extra.TagNo); + } } extras--; } diff --git a/crypto/src/bcpg/ECPublicBCPGKey.cs b/crypto/src/bcpg/ECPublicBCPGKey.cs index f328f9dc3..c473139e7 100644 --- a/crypto/src/bcpg/ECPublicBCPGKey.cs +++ b/crypto/src/bcpg/ECPublicBCPGKey.cs @@ -26,7 +26,7 @@ namespace Org.BouncyCastle.Bcpg DerObjectIdentifier oid, ECPoint point) { - this.point = new BigInteger(1, point.GetEncoded()); + this.point = new BigInteger(1, point.GetEncoded(false)); this.oid = oid; } diff --git a/crypto/src/cms/SignerInformation.cs b/crypto/src/cms/SignerInformation.cs index dad128263..39ecfa6d3 100644 --- a/crypto/src/cms/SignerInformation.cs +++ b/crypto/src/cms/SignerInformation.cs @@ -84,6 +84,30 @@ namespace Org.BouncyCastle.Cms this.digestCalculator = digestCalculator; } + /** + * Protected constructor. In some cases clients have their own idea about how to encode + * the signed attributes and calculate the signature. This constructor is to allow developers + * to deal with that by extending off the class and overridng methods like getSignedAttributes(). + * + * @param baseInfo the SignerInformation to base this one on. + */ + protected SignerInformation(SignerInformation baseInfo) + { + this.info = baseInfo.info; + this.contentType = baseInfo.contentType; + this.isCounterSignature = baseInfo.IsCounterSignature; + this.sid = baseInfo.SignerID; + this.digestAlgorithm = info.DigestAlgorithm; + this.signedAttributeSet = info.AuthenticatedAttributes; + this.unsignedAttributeSet = info.UnauthenticatedAttributes; + this.encryptionAlgorithm = info.DigestEncryptionAlgorithm; + this.signature = info.EncryptedDigest.GetOctets(); + this.content = baseInfo.content; + this.resultDigest = baseInfo.resultDigest; + this.signedAttributeTable = baseInfo.signedAttributeTable; + this.unsignedAttributeTable = baseInfo.unsignedAttributeTable; + } + public bool IsCounterSignature { get { return isCounterSignature; } diff --git a/crypto/src/crypto/IRawAgreement.cs b/crypto/src/crypto/IRawAgreement.cs new file mode 100644 index 000000000..63e664888 --- /dev/null +++ b/crypto/src/crypto/IRawAgreement.cs @@ -0,0 +1,13 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + public interface IRawAgreement + { + void Init(ICipherParameters parameters); + + int AgreementSize { get; } + + void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off); + } +} diff --git a/crypto/src/crypto/agreement/X25519Agreement.cs b/crypto/src/crypto/agreement/X25519Agreement.cs new file mode 100644 index 000000000..7e5890c16 --- /dev/null +++ b/crypto/src/crypto/agreement/X25519Agreement.cs @@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public sealed class X25519Agreement + : IRawAgreement + { + private X25519PrivateKeyParameters privateKey; + + public void Init(ICipherParameters parameters) + { + this.privateKey = (X25519PrivateKeyParameters)parameters; + } + + public int AgreementSize + { + get { return X25519PrivateKeyParameters.SecretSize; } + } + + public void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off) + { + privateKey.GenerateSecret((X25519PublicKeyParameters)publicKey, buf, off); + } + } +} diff --git a/crypto/src/crypto/agreement/X448Agreement.cs b/crypto/src/crypto/agreement/X448Agreement.cs new file mode 100644 index 000000000..26f608c26 --- /dev/null +++ b/crypto/src/crypto/agreement/X448Agreement.cs @@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public sealed class X448Agreement + : IRawAgreement + { + private X448PrivateKeyParameters privateKey; + + public void Init(ICipherParameters parameters) + { + this.privateKey = (X448PrivateKeyParameters)parameters; + } + + public int AgreementSize + { + get { return X448PrivateKeyParameters.SecretSize; } + } + + public void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off) + { + privateKey.GenerateSecret((X448PublicKeyParameters)publicKey, buf, off); + } + } +} diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs index b8e4f272e..770e35caf 100644 --- a/crypto/src/crypto/digests/Blake2bDigest.cs +++ b/crypto/src/crypto/digests/Blake2bDigest.cs @@ -136,8 +136,8 @@ namespace Org.BouncyCastle.Crypto.Digests */ public Blake2bDigest(int digestSize) { - if (digestSize != 160 && digestSize != 256 && digestSize != 384 && digestSize != 512) - throw new ArgumentException("BLAKE2b digest restricted to one of [160, 256, 384, 512]"); + if (digestSize < 8 || digestSize > 512 || digestSize % 8 != 0) + throw new ArgumentException("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512"); buffer = new byte[BLOCK_LENGTH_BYTES]; keyLength = 0; diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs index f31032874..432b0f4d2 100644 --- a/crypto/src/crypto/digests/Blake2sDigest.cs +++ b/crypto/src/crypto/digests/Blake2sDigest.cs @@ -152,13 +152,12 @@ namespace Org.BouncyCastle.Crypto.Digests /** * BLAKE2s for hashing. * - * @param digestBits the desired digest length in bits. Must be one of - * [128, 160, 224, 256]. + * @param digestBits the desired digest length in bits. Must be a multiple of 8 and less than 256. */ public Blake2sDigest(int digestBits) { - if (digestBits != 128 && digestBits != 160 && digestBits != 224 && digestBits != 256) - throw new ArgumentException("BLAKE2s digest restricted to one of [128, 160, 224, 256]"); + if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0) + throw new ArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256"); buffer = new byte[BLOCK_LENGTH_BYTES]; keyLength = 0; diff --git a/crypto/src/crypto/digests/NullDigest.cs b/crypto/src/crypto/digests/NullDigest.cs index e598cb145..76b69afbf 100644 --- a/crypto/src/crypto/digests/NullDigest.cs +++ b/crypto/src/crypto/digests/NullDigest.cs @@ -1,6 +1,8 @@ using System; using System.IO; +using Org.BouncyCastle.Utilities.IO; + namespace Org.BouncyCastle.Crypto.Digests { public class NullDigest : IDigest @@ -20,7 +22,7 @@ namespace Org.BouncyCastle.Crypto.Digests public int GetDigestSize() { - return (int) bOut.Length; + return (int)bOut.Length; } public void Update(byte b) @@ -33,15 +35,19 @@ namespace Org.BouncyCastle.Crypto.Digests bOut.Write(inBytes, inOff, len); } - public int DoFinal(byte[] outBytes, int outOff) + public int DoFinal(byte[] outBytes, int outOff) { - byte[] res = bOut.ToArray(); - res.CopyTo(outBytes, outOff); - Reset(); - return res.Length; - } - - public void Reset() + try + { + return Streams.WriteBufTo(bOut, outBytes, outOff); + } + finally + { + Reset(); + } + } + + public void Reset() { bOut.SetLength(0); } diff --git a/crypto/src/crypto/engines/RFC3211WrapEngine.cs b/crypto/src/crypto/engines/RFC3211WrapEngine.cs index 4e3af5227..86480145c 100644 --- a/crypto/src/crypto/engines/RFC3211WrapEngine.cs +++ b/crypto/src/crypto/engines/RFC3211WrapEngine.cs @@ -32,10 +32,10 @@ namespace Org.BouncyCastle.Crypto.Engines if (param is ParametersWithRandom) { - ParametersWithRandom p = (ParametersWithRandom) param; + ParametersWithRandom p = (ParametersWithRandom)param; - this.rand = p.Random; - this.param = (ParametersWithIV) p.Parameters; + this.rand = p.Random; + this.param = p.Parameters as ParametersWithIV; } else { @@ -44,9 +44,12 @@ namespace Org.BouncyCastle.Crypto.Engines rand = new SecureRandom(); } - this.param = (ParametersWithIV) param; - } - } + this.param = param as ParametersWithIV; + } + + if (null == this.param) + throw new ArgumentException("RFC3211Wrap requires an IV", "param"); + } public virtual string AlgorithmName { @@ -59,11 +62,11 @@ namespace Org.BouncyCastle.Crypto.Engines int inLen) { if (!forWrapping) - { throw new InvalidOperationException("not set for wrapping"); - } + if (inLen > 255 || inLen < 0) + throw new ArgumentException("input must be from 0 to 255 bytes", "inLen"); - engine.Init(true, param); + engine.Init(true, param); int blockSize = engine.GetBlockSize(); byte[] cekBlock; @@ -78,15 +81,16 @@ namespace Org.BouncyCastle.Crypto.Engines } cekBlock[0] = (byte)inLen; - cekBlock[1] = (byte)~inBytes[inOff]; - cekBlock[2] = (byte)~inBytes[inOff + 1]; - cekBlock[3] = (byte)~inBytes[inOff + 2]; Array.Copy(inBytes, inOff, cekBlock, 4, inLen); rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4); - for (int i = 0; i < cekBlock.Length; i += blockSize) + cekBlock[1] = (byte)~cekBlock[4]; + cekBlock[2] = (byte)~cekBlock[4 + 1]; + cekBlock[3] = (byte)~cekBlock[4 + 2]; + + for (int i = 0; i < cekBlock.Length; i += blockSize) { engine.ProcessBlock(cekBlock, i, cekBlock, i); } @@ -142,27 +146,34 @@ namespace Org.BouncyCastle.Crypto.Engines engine.ProcessBlock(cekBlock, i, cekBlock, i); } - if ((cekBlock[0] & 0xff) > cekBlock.Length - 4) - { - throw new InvalidCipherTextException("wrapped key corrupted"); - } + bool invalidLength = (int)cekBlock[0] > (cekBlock.Length - 4); - byte[] key = new byte[cekBlock[0] & 0xff]; + byte[] key; + if (invalidLength) + { + key = new byte[cekBlock.Length - 4]; + } + else + { + key = new byte[cekBlock[0]]; + } - Array.Copy(cekBlock, 4, key, 0, cekBlock[0]); + Array.Copy(cekBlock, 4, key, 0, key.Length); // Note: Using constant time comparison int nonEqual = 0; for (int i = 0; i != 3; i++) { byte check = (byte)~cekBlock[1 + i]; - nonEqual |= (check ^ key[i]); - } + nonEqual |= (check ^ cekBlock[4 + i]); + } + + Array.Clear(cekBlock, 0, cekBlock.Length); - if (nonEqual != 0) - throw new InvalidCipherTextException("wrapped key fails checksum"); + if (nonEqual != 0 | invalidLength) + throw new InvalidCipherTextException("wrapped key corrupted"); - return key; + return key; } } } diff --git a/crypto/src/crypto/engines/SM4Engine.cs b/crypto/src/crypto/engines/SM4Engine.cs new file mode 100644 index 000000000..7477b070e --- /dev/null +++ b/crypto/src/crypto/engines/SM4Engine.cs @@ -0,0 +1,189 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <summary>SM4 Block Cipher - SM4 is a 128 bit block cipher with a 128 bit key.</summary> + /// <remarks> + /// The implementation here is based on the document <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a> + /// by Whitfield Diffie and George Ledin, which is a translation of Prof. LU Shu-wang's original standard. + /// </remarks> + public class SM4Engine + : IBlockCipher + { + private const int BlockSize = 16; + + private static readonly byte[] Sbox = + { + 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, + 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, + 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, + 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, + 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, + 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, + 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, + 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, + 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, + 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, + 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, + 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, + 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, + 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, + 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, + 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48 + }; + + private static readonly uint[] CK = + { + 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, + 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, + 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, + 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, + 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, + 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, + 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, + 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 + }; + + private static readonly uint[] FK = + { + 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc + }; + + private uint[] rk; + + // non-linear substitution tau. + private static uint tau(uint A) + { + uint b0 = Sbox[A >> 24]; + uint b1 = Sbox[(A >> 16) & 0xFF]; + uint b2 = Sbox[(A >> 8) & 0xFF]; + uint b3 = Sbox[A & 0xFF]; + + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + private static uint L_ap(uint B) + { + return B ^ Integers.RotateLeft(B, 13) ^ Integers.RotateLeft(B, 23); + } + + private uint T_ap(uint Z) + { + return L_ap(tau(Z)); + } + + // Key expansion + private void ExpandKey(bool forEncryption, byte[] key) + { + uint K0 = Pack.BE_To_UInt32(key, 0) ^ FK[0]; + uint K1 = Pack.BE_To_UInt32(key, 4) ^ FK[1]; + uint K2 = Pack.BE_To_UInt32(key, 8) ^ FK[2]; + uint K3 = Pack.BE_To_UInt32(key, 12) ^ FK[3]; + + if (forEncryption) + { + rk[0] = K0 ^ T_ap(K1 ^ K2 ^ K3 ^ CK[0]); + rk[1] = K1 ^ T_ap(K2 ^ K3 ^ rk[0] ^ CK[1]); + rk[2] = K2 ^ T_ap(K3 ^ rk[0] ^ rk[1] ^ CK[2]); + rk[3] = K3 ^ T_ap(rk[0] ^ rk[1] ^ rk[2] ^ CK[3]); + for (int i = 4; i < 32; ++i) + { + rk[i] = rk[i - 4] ^ T_ap(rk[i - 3] ^ rk[i - 2] ^ rk[i - 1] ^ CK[i]); + } + } + else + { + rk[31] = K0 ^ T_ap(K1 ^ K2 ^ K3 ^ CK[0]); + rk[30] = K1 ^ T_ap(K2 ^ K3 ^ rk[31] ^ CK[1]); + rk[29] = K2 ^ T_ap(K3 ^ rk[31] ^ rk[30] ^ CK[2]); + rk[28] = K3 ^ T_ap(rk[31] ^ rk[30] ^ rk[29] ^ CK[3]); + for (int i = 27; i >= 0; --i) + { + rk[i] = rk[i + 4] ^ T_ap(rk[i + 3] ^ rk[i + 2] ^ rk[i + 1] ^ CK[31 - i]); + } + } + } + + // Linear substitution L + private static uint L(uint B) + { + return B ^ Integers.RotateLeft(B, 2) ^ Integers.RotateLeft(B, 10) ^ Integers.RotateLeft(B, 18) ^ Integers.RotateLeft(B, 24); + } + + // Mixer-substitution T + private static uint T(uint Z) + { + return L(tau(Z)); + } + + public virtual void Init(bool forEncryption, ICipherParameters parameters) + { + KeyParameter keyParameter = parameters as KeyParameter; + if (null == keyParameter) + throw new ArgumentException("invalid parameter passed to SM4 init - " + Platform.GetTypeName(parameters), "parameters"); + + byte[] key = keyParameter.GetKey(); + if (key.Length != 16) + throw new ArgumentException("SM4 requires a 128 bit key", "parameters"); + + if (null == rk) + { + rk = new uint[32]; + } + + ExpandKey(forEncryption, key); + } + + public virtual string AlgorithmName + { + get { return "SM4"; } + } + + public virtual bool IsPartialBlockOkay + { + get { return false; } + } + + public virtual int GetBlockSize() + { + return BlockSize; + } + + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) + { + if (null == rk) + throw new InvalidOperationException("SM4 not initialised"); + + Check.DataLength(input, inOff, BlockSize, "input buffer too short"); + Check.OutputLength(output, outOff, BlockSize, "output buffer too short"); + + uint X0 = Pack.BE_To_UInt32(input, inOff); + uint X1 = Pack.BE_To_UInt32(input, inOff + 4); + uint X2 = Pack.BE_To_UInt32(input, inOff + 8); + uint X3 = Pack.BE_To_UInt32(input, inOff + 12); + + for (int i = 0; i < 32; i += 4) + { + X0 ^= T(X1 ^ X2 ^ X3 ^ rk[i ]); // F0 + X1 ^= T(X2 ^ X3 ^ X0 ^ rk[i + 1]); // F1 + X2 ^= T(X3 ^ X0 ^ X1 ^ rk[i + 2]); // F2 + X3 ^= T(X0 ^ X1 ^ X2 ^ rk[i + 3]); // F3 + } + + Pack.UInt32_To_BE(X3, output, outOff); + Pack.UInt32_To_BE(X2, output, outOff + 4); + Pack.UInt32_To_BE(X1, output, outOff + 8); + Pack.UInt32_To_BE(X0, output, outOff + 12); + + return BlockSize; + } + + public virtual void Reset() + { + } + } +} diff --git a/crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs b/crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs new file mode 100644 index 000000000..266d111cf --- /dev/null +++ b/crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs @@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class Ed25519KeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private SecureRandom random; + + public virtual void Init(KeyGenerationParameters parameters) + { + this.random = parameters.Random; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(random); + Ed25519PublicKeyParameters publicKey = privateKey.GeneratePublicKey(); + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } + } +} diff --git a/crypto/src/crypto/generators/Ed448KeyPairGenerator.cs b/crypto/src/crypto/generators/Ed448KeyPairGenerator.cs new file mode 100644 index 000000000..50aee631e --- /dev/null +++ b/crypto/src/crypto/generators/Ed448KeyPairGenerator.cs @@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class Ed448KeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private SecureRandom random; + + public virtual void Init(KeyGenerationParameters parameters) + { + this.random = parameters.Random; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + Ed448PrivateKeyParameters privateKey = new Ed448PrivateKeyParameters(random); + Ed448PublicKeyParameters publicKey = privateKey.GeneratePublicKey(); + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } + } +} diff --git a/crypto/src/crypto/generators/X25519KeyPairGenerator.cs b/crypto/src/crypto/generators/X25519KeyPairGenerator.cs new file mode 100644 index 000000000..94378448b --- /dev/null +++ b/crypto/src/crypto/generators/X25519KeyPairGenerator.cs @@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class X25519KeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private SecureRandom random; + + public virtual void Init(KeyGenerationParameters parameters) + { + this.random = parameters.Random; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + X25519PrivateKeyParameters privateKey = new X25519PrivateKeyParameters(random); + X25519PublicKeyParameters publicKey = privateKey.GeneratePublicKey(); + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } + } +} diff --git a/crypto/src/crypto/generators/X448KeyPairGenerator.cs b/crypto/src/crypto/generators/X448KeyPairGenerator.cs new file mode 100644 index 000000000..4a203e4f1 --- /dev/null +++ b/crypto/src/crypto/generators/X448KeyPairGenerator.cs @@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class X448KeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private SecureRandom random; + + public virtual void Init(KeyGenerationParameters parameters) + { + this.random = parameters.Random; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + X448PrivateKeyParameters privateKey = new X448PrivateKeyParameters(random); + X448PublicKeyParameters publicKey = privateKey.GeneratePublicKey(); + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } + } +} diff --git a/crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs b/crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs new file mode 100644 index 000000000..daf3856c3 --- /dev/null +++ b/crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs @@ -0,0 +1,15 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Ed25519KeyGenerationParameters + : KeyGenerationParameters + { + public Ed25519KeyGenerationParameters(SecureRandom random) + : base(random, 256) + { + } + } +} diff --git a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs new file mode 100644 index 000000000..97902e093 --- /dev/null +++ b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class Ed25519PrivateKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = Ed25519.SecretKeySize; + public static readonly int SignatureSize = Ed25519.SignatureSize; + + private readonly byte[] data = new byte[KeySize]; + + public Ed25519PrivateKeyParameters(SecureRandom random) + : base(true) + { + random.NextBytes(data); + } + + public Ed25519PrivateKeyParameters(byte[] buf, int off) + : base(true) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public Ed25519PrivateKeyParameters(Stream input) + : base(true) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of Ed25519 private key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + + public Ed25519PublicKeyParameters GeneratePublicKey() + { + byte[] publicKey = new byte[Ed25519.PublicKeySize]; + Ed25519.GeneratePublicKey(data, 0, publicKey, 0); + return new Ed25519PublicKeyParameters(publicKey, 0); + } + + public void Sign(Ed25519.Algorithm algorithm, Ed25519PublicKeyParameters publicKey, byte[] ctx, byte[] msg, int msgOff, int msgLen, + byte[] sig, int sigOff) + { + byte[] pk = new byte[Ed25519.PublicKeySize]; + if (null == publicKey) + { + Ed25519.GeneratePublicKey(data, 0, pk, 0); + } + else + { + publicKey.Encode(pk, 0); + } + + switch (algorithm) + { + case Ed25519.Algorithm.Ed25519: + { + if (null != ctx) + throw new ArgumentException("ctx"); + + Ed25519.Sign(data, 0, pk, 0, msg, msgOff, msgLen, sig, sigOff); + break; + } + case Ed25519.Algorithm.Ed25519ctx: + { + Ed25519.Sign(data, 0, pk, 0, ctx, msg, msgOff, msgLen, sig, sigOff); + break; + } + case Ed25519.Algorithm.Ed25519ph: + { + if (Ed25519.PrehashSize != msgLen) + throw new ArgumentException("msgLen"); + + Ed25519.SignPrehash(data, 0, pk, 0, ctx, msg, msgOff, sig, sigOff); + break; + } + default: + { + throw new ArgumentException("algorithm"); + } + } + } + } +} diff --git a/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs new file mode 100644 index 000000000..96e9ec21f --- /dev/null +++ b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class Ed25519PublicKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = Ed25519.PublicKeySize; + + private readonly byte[] data = new byte[KeySize]; + + public Ed25519PublicKeyParameters(byte[] buf, int off) + : base(false) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public Ed25519PublicKeyParameters(Stream input) + : base(false) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of Ed25519 public key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + } +} diff --git a/crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs b/crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs new file mode 100644 index 000000000..830d15a04 --- /dev/null +++ b/crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs @@ -0,0 +1,15 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Ed448KeyGenerationParameters + : KeyGenerationParameters + { + public Ed448KeyGenerationParameters(SecureRandom random) + : base(random, 448) + { + } + } +} diff --git a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs new file mode 100644 index 000000000..74b5d63f3 --- /dev/null +++ b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class Ed448PrivateKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = Ed448.SecretKeySize; + public static readonly int SignatureSize = Ed448.SignatureSize; + + private readonly byte[] data = new byte[KeySize]; + + public Ed448PrivateKeyParameters(SecureRandom random) + : base(true) + { + random.NextBytes(data); + } + + public Ed448PrivateKeyParameters(byte[] buf, int off) + : base(true) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public Ed448PrivateKeyParameters(Stream input) + : base(true) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of Ed448 private key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + + public Ed448PublicKeyParameters GeneratePublicKey() + { + byte[] publicKey = new byte[Ed448.PublicKeySize]; + Ed448.GeneratePublicKey(data, 0, publicKey, 0); + return new Ed448PublicKeyParameters(publicKey, 0); + } + + public void Sign(Ed448.Algorithm algorithm, Ed448PublicKeyParameters publicKey, byte[] ctx, byte[] msg, int msgOff, int msgLen, + byte[] sig, int sigOff) + { + byte[] pk = new byte[Ed448.PublicKeySize]; + if (null == publicKey) + { + Ed448.GeneratePublicKey(data, 0, pk, 0); + } + else + { + publicKey.Encode(pk, 0); + } + + switch (algorithm) + { + case Ed448.Algorithm.Ed448: + { + Ed448.Sign(data, 0, pk, 0, ctx, msg, msgOff, msgLen, sig, sigOff); + break; + } + case Ed448.Algorithm.Ed448ph: + { + if (Ed448.PrehashSize != msgLen) + throw new ArgumentException("msgLen"); + + Ed448.SignPrehash(data, 0, pk, 0, ctx, msg, msgOff, sig, sigOff); + break; + } + default: + { + throw new ArgumentException("algorithm"); + } + } + } + } +} diff --git a/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs new file mode 100644 index 000000000..d7faac246 --- /dev/null +++ b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class Ed448PublicKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = Ed448.PublicKeySize; + + private readonly byte[] data = new byte[KeySize]; + + public Ed448PublicKeyParameters(byte[] buf, int off) + : base(false) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public Ed448PublicKeyParameters(Stream input) + : base(false) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of Ed448 public key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + } +} diff --git a/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs b/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs index 7bd8abd76..557ee94e2 100644 --- a/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs +++ b/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs @@ -2,33 +2,34 @@ using System; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Asn1.Pkcs; namespace Org.BouncyCastle.Crypto.Parameters { public class RsaPrivateCrtKeyParameters - : RsaKeyParameters + : RsaKeyParameters { private readonly BigInteger e, p, q, dP, dQ, qInv; - public RsaPrivateCrtKeyParameters( - BigInteger modulus, - BigInteger publicExponent, - BigInteger privateExponent, - BigInteger p, - BigInteger q, - BigInteger dP, - BigInteger dQ, - BigInteger qInv) - : base(true, modulus, privateExponent) + public RsaPrivateCrtKeyParameters( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger p, + BigInteger q, + BigInteger dP, + BigInteger dQ, + BigInteger qInv) + : base(true, modulus, privateExponent) { - ValidateValue(publicExponent, "publicExponent", "exponent"); - ValidateValue(p, "p", "P value"); - ValidateValue(q, "q", "Q value"); - ValidateValue(dP, "dP", "DP value"); - ValidateValue(dQ, "dQ", "DQ value"); - ValidateValue(qInv, "qInv", "InverseQ value"); - - this.e = publicExponent; + ValidateValue(publicExponent, "publicExponent", "exponent"); + ValidateValue(p, "p", "P value"); + ValidateValue(q, "q", "Q value"); + ValidateValue(dP, "dP", "DP value"); + ValidateValue(dQ, "dQ", "DQ value"); + ValidateValue(qInv, "qInv", "InverseQ value"); + + this.e = publicExponent; this.p = p; this.q = q; this.dP = dP; @@ -36,69 +37,82 @@ namespace Org.BouncyCastle.Crypto.Parameters this.qInv = qInv; } - public BigInteger PublicExponent + public RsaPrivateCrtKeyParameters(RsaPrivateKeyStructure rsaPrivateKey) + : this( + rsaPrivateKey.Modulus, + rsaPrivateKey.PublicExponent, + rsaPrivateKey.PrivateExponent, + rsaPrivateKey.Prime1, + rsaPrivateKey.Prime2, + rsaPrivateKey.Exponent1, + rsaPrivateKey.Exponent2, + rsaPrivateKey.Coefficient) + { + } + + public BigInteger PublicExponent { get { return e; } - } - - public BigInteger P - { - get { return p; } - } - - public BigInteger Q - { - get { return q; } - } - - public BigInteger DP - { - get { return dP; } - } - - public BigInteger DQ - { - get { return dQ; } - } - - public BigInteger QInv - { - get { return qInv; } - } - - public override bool Equals( - object obj) - { - if (obj == this) - return true; - - RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters; - - if (kp == null) - return false; - - return kp.DP.Equals(dP) - && kp.DQ.Equals(dQ) - && kp.Exponent.Equals(this.Exponent) - && kp.Modulus.Equals(this.Modulus) - && kp.P.Equals(p) - && kp.Q.Equals(q) - && kp.PublicExponent.Equals(e) - && kp.QInv.Equals(qInv); - } - - public override int GetHashCode() - { - return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode() - ^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode(); - } - - private static void ValidateValue(BigInteger x, string name, string desc) - { - if (x == null) - throw new ArgumentNullException(name); - if (x.SignValue <= 0) - throw new ArgumentException("Not a valid RSA " + desc, name); - } - } + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger DP + { + get { return dP; } + } + + public BigInteger DQ + { + get { return dQ; } + } + + public BigInteger QInv + { + get { return qInv; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters; + + if (kp == null) + return false; + + return kp.DP.Equals(dP) + && kp.DQ.Equals(dQ) + && kp.Exponent.Equals(this.Exponent) + && kp.Modulus.Equals(this.Modulus) + && kp.P.Equals(p) + && kp.Q.Equals(q) + && kp.PublicExponent.Equals(e) + && kp.QInv.Equals(qInv); + } + + public override int GetHashCode() + { + return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode() + ^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode(); + } + + private static void ValidateValue(BigInteger x, string name, string desc) + { + if (x == null) + throw new ArgumentNullException(name); + if (x.SignValue <= 0) + throw new ArgumentException("Not a valid RSA " + desc, name); + } + } } diff --git a/crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs b/crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs new file mode 100644 index 000000000..09972c7a2 --- /dev/null +++ b/crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs @@ -0,0 +1,15 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class X25519KeyGenerationParameters + : KeyGenerationParameters + { + public X25519KeyGenerationParameters(SecureRandom random) + : base(random, 256) + { + } + } +} diff --git a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs new file mode 100644 index 000000000..fb49a02b3 --- /dev/null +++ b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc7748; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class X25519PrivateKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = X25519.ScalarSize; + public static readonly int SecretSize = X25519.PointSize; + + private readonly byte[] data = new byte[KeySize]; + + public X25519PrivateKeyParameters(SecureRandom random) + : base(true) + { + random.NextBytes(data); + } + + public X25519PrivateKeyParameters(byte[] buf, int off) + : base(true) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public X25519PrivateKeyParameters(Stream input) + : base(true) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of X25519 private key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + + public X25519PublicKeyParameters GeneratePublicKey() + { + byte[] publicKey = new byte[X25519.PointSize]; + X25519.ScalarMultBase(data, 0, publicKey, 0); + return new X25519PublicKeyParameters(publicKey, 0); + } + + public void GenerateSecret(X25519PublicKeyParameters publicKey, byte[] buf, int off) + { + byte[] encoded = new byte[X25519.PointSize]; + publicKey.Encode(encoded, 0); + if (!X25519.CalculateAgreement(data, 0, encoded, 0, buf, off)) + throw new InvalidOperationException("X25519 agreement failed"); + } + } +} diff --git a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs new file mode 100644 index 000000000..7df5f624d --- /dev/null +++ b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc7748; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class X25519PublicKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = X25519.PointSize; + + private readonly byte[] data = new byte[KeySize]; + + public X25519PublicKeyParameters(byte[] buf, int off) + : base(false) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public X25519PublicKeyParameters(Stream input) + : base(false) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of X25519 public key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + } +} diff --git a/crypto/src/crypto/parameters/X448KeyGenerationParameters.cs b/crypto/src/crypto/parameters/X448KeyGenerationParameters.cs new file mode 100644 index 000000000..a7cb55844 --- /dev/null +++ b/crypto/src/crypto/parameters/X448KeyGenerationParameters.cs @@ -0,0 +1,15 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class X448KeyGenerationParameters + : KeyGenerationParameters + { + public X448KeyGenerationParameters(SecureRandom random) + : base(random, 448) + { + } + } +} diff --git a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs new file mode 100644 index 000000000..d17aa7947 --- /dev/null +++ b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc7748; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class X448PrivateKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = X448.ScalarSize; + public static readonly int SecretSize = X448.PointSize; + + private readonly byte[] data = new byte[KeySize]; + + public X448PrivateKeyParameters(SecureRandom random) + : base(true) + { + random.NextBytes(data); + } + + public X448PrivateKeyParameters(byte[] buf, int off) + : base(true) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public X448PrivateKeyParameters(Stream input) + : base(true) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of X448 private key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + + public X448PublicKeyParameters GeneratePublicKey() + { + byte[] publicKey = new byte[X448.PointSize]; + X448.ScalarMultBase(data, 0, publicKey, 0); + return new X448PublicKeyParameters(publicKey, 0); + } + + public void GenerateSecret(X448PublicKeyParameters publicKey, byte[] buf, int off) + { + byte[] encoded = new byte[X448.PointSize]; + publicKey.Encode(encoded, 0); + if (!X448.CalculateAgreement(data, 0, encoded, 0, buf, off)) + throw new InvalidOperationException("X448 agreement failed"); + } + } +} diff --git a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs new file mode 100644 index 000000000..6c566ddf2 --- /dev/null +++ b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math.EC.Rfc7748; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public sealed class X448PublicKeyParameters + : AsymmetricKeyParameter + { + public static readonly int KeySize = X448.PointSize; + + private readonly byte[] data = new byte[KeySize]; + + public X448PublicKeyParameters(byte[] buf, int off) + : base(false) + { + Array.Copy(buf, off, data, 0, KeySize); + } + + public X448PublicKeyParameters(Stream input) + : base(false) + { + if (KeySize != Streams.ReadFully(input, data)) + throw new EndOfStreamException("EOF encountered in middle of X448 public key"); + } + + public void Encode(byte[] buf, int off) + { + Array.Copy(data, 0, buf, off, KeySize); + } + + public byte[] GetEncoded() + { + return Arrays.Clone(data); + } + } +} diff --git a/crypto/src/crypto/signers/Ed25519Signer.cs b/crypto/src/crypto/signers/Ed25519Signer.cs new file mode 100644 index 000000000..1b3142c7b --- /dev/null +++ b/crypto/src/crypto/signers/Ed25519Signer.cs @@ -0,0 +1,129 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Ed25519Signer + : ISigner + { + private readonly Buffer buffer = new Buffer(); + + private bool forSigning; + private Ed25519PrivateKeyParameters privateKey; + private Ed25519PublicKeyParameters publicKey; + + public Ed25519Signer() + { + } + + public virtual string AlgorithmName + { + get { return "Ed25519"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + // TODO Allow IAsymmetricCipherKeyPair to be an ICipherParameters? + + this.privateKey = (Ed25519PrivateKeyParameters)parameters; + this.publicKey = privateKey.GeneratePublicKey(); + } + else + { + this.privateKey = null; + this.publicKey = (Ed25519PublicKeyParameters)parameters; + } + + Reset(); + } + + public virtual void Update(byte b) + { + buffer.WriteByte(b); + } + + public virtual void BlockUpdate(byte[] buf, int off, int len) + { + buffer.Write(buf, off, len); + } + + public virtual byte[] GenerateSignature() + { + if (!forSigning || null == privateKey) + throw new InvalidOperationException("Ed25519Signer not initialised for signature generation."); + + return buffer.GenerateSignature(privateKey, publicKey); + } + + public virtual bool VerifySignature(byte[] signature) + { + if (forSigning || null == publicKey) + throw new InvalidOperationException("Ed25519Signer not initialised for verification"); + + return buffer.VerifySignature(publicKey, signature); + } + + public virtual void Reset() + { + buffer.Reset(); + } + + private class Buffer : MemoryStream + { + [MethodImpl(MethodImplOptions.Synchronized)] + internal byte[] GenerateSignature(Ed25519PrivateKeyParameters privateKey, Ed25519PublicKeyParameters publicKey) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Position; +#endif + byte[] signature = new byte[Ed25519PrivateKeyParameters.SignatureSize]; + privateKey.Sign(Ed25519.Algorithm.Ed25519, publicKey, null, buf, 0, count, signature, 0); + Reset(); + return signature; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + internal bool VerifySignature(Ed25519PublicKeyParameters publicKey, byte[] signature) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Position; +#endif + byte[] pk = publicKey.GetEncoded(); + bool result = Ed25519.Verify(signature, 0, pk, 0, buf, 0, count); + Reset(); + return result; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Reset() + { + long count = Position; +#if PORTABLE + this.Position = 0L; + Streams.WriteZeroes(this, count); +#else + Array.Clear(GetBuffer(), 0, (int)count); +#endif + this.Position = 0L; + } + } + } +} diff --git a/crypto/src/crypto/signers/Ed25519ctxSigner.cs b/crypto/src/crypto/signers/Ed25519ctxSigner.cs new file mode 100644 index 000000000..965453011 --- /dev/null +++ b/crypto/src/crypto/signers/Ed25519ctxSigner.cs @@ -0,0 +1,131 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Ed25519ctxSigner + : ISigner + { + private readonly Buffer buffer = new Buffer(); + private readonly byte[] context; + + private bool forSigning; + private Ed25519PrivateKeyParameters privateKey; + private Ed25519PublicKeyParameters publicKey; + + public Ed25519ctxSigner(byte[] context) + { + this.context = Arrays.Clone(context); + } + + public virtual string AlgorithmName + { + get { return "Ed25519ctx"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + // TODO Allow IAsymmetricCipherKeyPair to be an ICipherParameters? + + this.privateKey = (Ed25519PrivateKeyParameters)parameters; + this.publicKey = privateKey.GeneratePublicKey(); + } + else + { + this.privateKey = null; + this.publicKey = (Ed25519PublicKeyParameters)parameters; + } + + Reset(); + } + + public virtual void Update(byte b) + { + buffer.WriteByte(b); + } + + public virtual void BlockUpdate(byte[] buf, int off, int len) + { + buffer.Write(buf, off, len); + } + + public virtual byte[] GenerateSignature() + { + if (!forSigning || null == privateKey) + throw new InvalidOperationException("Ed25519ctxSigner not initialised for signature generation."); + + return buffer.GenerateSignature(privateKey, publicKey, context); + } + + public virtual bool VerifySignature(byte[] signature) + { + if (forSigning || null == publicKey) + throw new InvalidOperationException("Ed25519ctxSigner not initialised for verification"); + + return buffer.VerifySignature(publicKey, context, signature); + } + + public virtual void Reset() + { + buffer.Reset(); + } + + private class Buffer : MemoryStream + { + [MethodImpl(MethodImplOptions.Synchronized)] + internal byte[] GenerateSignature(Ed25519PrivateKeyParameters privateKey, Ed25519PublicKeyParameters publicKey, byte[] ctx) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Position; +#endif + byte[] signature = new byte[Ed25519PrivateKeyParameters.SignatureSize]; + privateKey.Sign(Ed25519.Algorithm.Ed25519ctx, publicKey, ctx, buf, 0, count, signature, 0); + Reset(); + return signature; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + internal bool VerifySignature(Ed25519PublicKeyParameters publicKey, byte[] ctx, byte[] signature) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Position; +#endif + byte[] pk = publicKey.GetEncoded(); + bool result = Ed25519.Verify(signature, 0, pk, 0, ctx, buf, 0, count); + Reset(); + return result; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Reset() + { + long count = Position; +#if PORTABLE + this.Position = 0L; + Streams.WriteZeroes(this, count); +#else + Array.Clear(GetBuffer(), 0, (int)count); +#endif + this.Position = 0L; + } + } + } +} diff --git a/crypto/src/crypto/signers/Ed25519phSigner.cs b/crypto/src/crypto/signers/Ed25519phSigner.cs new file mode 100644 index 000000000..3318f6438 --- /dev/null +++ b/crypto/src/crypto/signers/Ed25519phSigner.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Ed25519phSigner + : ISigner + { + private readonly IDigest prehash = Ed25519.CreatePrehash(); + private readonly byte[] context; + + private bool forSigning; + private Ed25519PrivateKeyParameters privateKey; + private Ed25519PublicKeyParameters publicKey; + + public Ed25519phSigner(byte[] context) + { + this.context = Arrays.Clone(context); + } + + public virtual string AlgorithmName + { + get { return "Ed25519ph"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters? + + this.privateKey = (Ed25519PrivateKeyParameters)parameters; + this.publicKey = privateKey.GeneratePublicKey(); + } + else + { + this.privateKey = null; + this.publicKey = (Ed25519PublicKeyParameters)parameters; + } + + Reset(); + } + + public virtual void Update(byte b) + { + prehash.Update(b); + } + + public virtual void BlockUpdate(byte[] buf, int off, int len) + { + prehash.BlockUpdate(buf, off, len); + } + + public virtual byte[] GenerateSignature() + { + if (!forSigning || null == privateKey) + throw new InvalidOperationException("Ed25519phSigner not initialised for signature generation."); + + byte[] msg = new byte[Ed25519.PrehashSize]; + if (Ed25519.PrehashSize != prehash.DoFinal(msg, 0)) + throw new InvalidOperationException("Prehash digest failed"); + + byte[] signature = new byte[Ed25519PrivateKeyParameters.SignatureSize]; + privateKey.Sign(Ed25519.Algorithm.Ed25519ph, publicKey, context, msg, 0, Ed25519.PrehashSize, signature, 0); + return signature; + } + + public virtual bool VerifySignature(byte[] signature) + { + if (forSigning || null == publicKey) + throw new InvalidOperationException("Ed25519phSigner not initialised for verification"); + + byte[] pk = publicKey.GetEncoded(); + return Ed25519.VerifyPrehash(signature, 0, pk, 0, context, prehash); + } + + public void Reset() + { + prehash.Reset(); + } + } +} diff --git a/crypto/src/crypto/signers/Ed448Signer.cs b/crypto/src/crypto/signers/Ed448Signer.cs new file mode 100644 index 000000000..d18f956a8 --- /dev/null +++ b/crypto/src/crypto/signers/Ed448Signer.cs @@ -0,0 +1,131 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Ed448Signer + : ISigner + { + private readonly Buffer buffer = new Buffer(); + private readonly byte[] context; + + private bool forSigning; + private Ed448PrivateKeyParameters privateKey; + private Ed448PublicKeyParameters publicKey; + + public Ed448Signer(byte[] context) + { + this.context = Arrays.Clone(context); + } + + public virtual string AlgorithmName + { + get { return "Ed448"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + // TODO Allow IAsymmetricCipherKeyPair to be an ICipherParameters? + + this.privateKey = (Ed448PrivateKeyParameters)parameters; + this.publicKey = privateKey.GeneratePublicKey(); + } + else + { + this.privateKey = null; + this.publicKey = (Ed448PublicKeyParameters)parameters; + } + + Reset(); + } + + public virtual void Update(byte b) + { + buffer.WriteByte(b); + } + + public virtual void BlockUpdate(byte[] buf, int off, int len) + { + buffer.Write(buf, off, len); + } + + public virtual byte[] GenerateSignature() + { + if (!forSigning || null == privateKey) + throw new InvalidOperationException("Ed448Signer not initialised for signature generation."); + + return buffer.GenerateSignature(privateKey, publicKey, context); + } + + public virtual bool VerifySignature(byte[] signature) + { + if (forSigning || null == publicKey) + throw new InvalidOperationException("Ed448Signer not initialised for verification"); + + return buffer.VerifySignature(publicKey, context, signature); + } + + public virtual void Reset() + { + buffer.Reset(); + } + + private class Buffer : MemoryStream + { + [MethodImpl(MethodImplOptions.Synchronized)] + internal byte[] GenerateSignature(Ed448PrivateKeyParameters privateKey, Ed448PublicKeyParameters publicKey, byte[] ctx) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Position; +#endif + byte[] signature = new byte[Ed448PrivateKeyParameters.SignatureSize]; + privateKey.Sign(Ed448.Algorithm.Ed448, publicKey, ctx, buf, 0, count, signature, 0); + Reset(); + return signature; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + internal bool VerifySignature(Ed448PublicKeyParameters publicKey, byte[] ctx, byte[] signature) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Position; +#endif + byte[] pk = publicKey.GetEncoded(); + bool result = Ed448.Verify(signature, 0, pk, 0, ctx, buf, 0, count); + Reset(); + return result; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Reset() + { + long count = Position; +#if PORTABLE + this.Position = 0L; + Streams.WriteZeroes(this, count); +#else + Array.Clear(GetBuffer(), 0, (int)count); +#endif + this.Position = 0L; + } + } + } +} diff --git a/crypto/src/crypto/signers/Ed448phSigner.cs b/crypto/src/crypto/signers/Ed448phSigner.cs new file mode 100644 index 000000000..b86d0855c --- /dev/null +++ b/crypto/src/crypto/signers/Ed448phSigner.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Ed448phSigner + : ISigner + { + private readonly IXof prehash = Ed448.CreatePrehash(); + private readonly byte[] context; + + private bool forSigning; + private Ed448PrivateKeyParameters privateKey; + private Ed448PublicKeyParameters publicKey; + + public Ed448phSigner(byte[] context) + { + this.context = Arrays.Clone(context); + } + + public virtual string AlgorithmName + { + get { return "Ed448ph"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters? + + this.privateKey = (Ed448PrivateKeyParameters)parameters; + this.publicKey = privateKey.GeneratePublicKey(); + } + else + { + this.privateKey = null; + this.publicKey = (Ed448PublicKeyParameters)parameters; + } + + Reset(); + } + + public virtual void Update(byte b) + { + prehash.Update(b); + } + + public virtual void BlockUpdate(byte[] buf, int off, int len) + { + prehash.BlockUpdate(buf, off, len); + } + + public virtual byte[] GenerateSignature() + { + if (!forSigning || null == privateKey) + throw new InvalidOperationException("Ed448phSigner not initialised for signature generation."); + + byte[] msg = new byte[Ed448.PrehashSize]; + if (Ed448.PrehashSize != prehash.DoFinal(msg, 0, Ed448.PrehashSize)) + throw new InvalidOperationException("Prehash digest failed"); + + byte[] signature = new byte[Ed448PrivateKeyParameters.SignatureSize]; + privateKey.Sign(Ed448.Algorithm.Ed448ph, publicKey, context, msg, 0, Ed448.PrehashSize, signature, 0); + return signature; + } + + public virtual bool VerifySignature(byte[] signature) + { + if (forSigning || null == publicKey) + throw new InvalidOperationException("Ed448phSigner not initialised for verification"); + + byte[] pk = publicKey.GetEncoded(); + return Ed448.VerifyPrehash(signature, 0, pk, 0, context, prehash); + } + + public void Reset() + { + prehash.Reset(); + } + } +} diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs index 72151d414..bbb76d53c 100644 --- a/crypto/src/crypto/tls/TlsProtocol.cs +++ b/crypto/src/crypto/tls/TlsProtocol.cs @@ -391,31 +391,30 @@ namespace Org.BouncyCastle.Crypto.Tls if (queue.Available < totalLength) break; - CheckReceivedChangeCipherSpec(mConnectionState == CS_END || type == HandshakeType.finished); - /* * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages * starting at client hello up to, but not including, this finished message. * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes. */ - switch (type) - { - case HandshakeType.hello_request: - break; - case HandshakeType.finished: - default: + if (HandshakeType.hello_request != type) { - TlsContext ctx = Context; - if (type == HandshakeType.finished - && this.mExpectedVerifyData == null - && ctx.SecurityParameters.MasterSecret != null) + if (HandshakeType.finished == type) { - this.mExpectedVerifyData = CreateVerifyData(!ctx.IsServer); + CheckReceivedChangeCipherSpec(true); + + TlsContext ctx = Context; + if (this.mExpectedVerifyData == null + && ctx.SecurityParameters.MasterSecret != null) + { + this.mExpectedVerifyData = CreateVerifyData(!ctx.IsServer); + } + } + else + { + CheckReceivedChangeCipherSpec(mConnectionState == CS_END); } queue.CopyTo(mRecordStream.HandshakeHashUpdater, totalLength); - break; - } } queue.RemoveData(4); diff --git a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs index 505832442..37e5b5c29 100644 --- a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs +++ b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs @@ -37,15 +37,17 @@ namespace Org.BouncyCastle.Math.EC.Multiplier int top = fullComb - 1; for (int i = 0; i < d; ++i) { - int secretIndex = 0; + uint secretIndex = 0; for (int j = top - i; j >= 0; j -= d) { + uint secretBit = K[j >> 5] >> (j & 0x1F); + secretIndex ^= secretBit >> 1; secretIndex <<= 1; - secretIndex |= (int)Nat.GetBit(K, j); + secretIndex ^= secretBit; } - ECPoint add = lookupTable.Lookup(secretIndex); + ECPoint add = lookupTable.Lookup((int)secretIndex); R = R.TwicePlus(add); } diff --git a/crypto/src/math/ec/multiplier/WNafUtilities.cs b/crypto/src/math/ec/multiplier/WNafUtilities.cs index e893abd49..24646deb2 100644 --- a/crypto/src/math/ec/multiplier/WNafUtilities.cs +++ b/crypto/src/math/ec/multiplier/WNafUtilities.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Math.EC.Multiplier { public abstract class WNafUtilities @@ -8,8 +10,6 @@ namespace Org.BouncyCastle.Math.EC.Multiplier private static readonly int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; - private static readonly byte[] EMPTY_BYTES = new byte[0]; - private static readonly int[] EMPTY_INTS = new int[0]; private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0]; public static int[] GenerateCompactNaf(BigInteger k) @@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier if ((k.BitLength >> 16) != 0) throw new ArgumentException("must have bitlength < 2^16", "k"); if (k.SignValue == 0) - return EMPTY_INTS; + return Arrays.EmptyInts; BigInteger _3k = k.ShiftLeft(1).Add(k); @@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier if ((k.BitLength >> 16) != 0) throw new ArgumentException("must have bitlength < 2^16", "k"); if (k.SignValue == 0) - return EMPTY_INTS; + return Arrays.EmptyInts; int[] wnaf = new int[k.BitLength / width + 1]; @@ -176,7 +176,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier public static byte[] GenerateNaf(BigInteger k) { if (k.SignValue == 0) - return EMPTY_BYTES; + return Arrays.EmptyBytes; BigInteger _3k = k.ShiftLeft(1).Add(k); @@ -221,7 +221,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier if (width < 2 || width > 8) throw new ArgumentException("must be in the range [2, 8]", "width"); if (k.SignValue == 0) - return EMPTY_BYTES; + return Arrays.EmptyBytes; byte[] wnaf = new byte[k.BitLength + 1]; diff --git a/crypto/src/math/ec/rfc7748/X25519.cs b/crypto/src/math/ec/rfc7748/X25519.cs index a10d53da5..6b6acdecd 100644 --- a/crypto/src/math/ec/rfc7748/X25519.cs +++ b/crypto/src/math/ec/rfc7748/X25519.cs @@ -2,10 +2,15 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Math.EC.Rfc7748 { public abstract class X25519 { + public const int PointSize = 32; + public const int ScalarSize = 32; + private const int C_A = 486662; private const int C_A24 = (C_A + 2)/4; @@ -18,6 +23,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 private static int[] precompBase = null; + public static bool CalculateAgreement(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff) + { + ScalarMult(k, kOff, u, uOff, r, rOff); + return !Arrays.AreAllZeroes(r, rOff, PointSize); + } + private static uint Decode32(byte[] bs, int off) { uint n = bs[off]; diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs index 88e8a5d76..b93cb24c5 100644 --- a/crypto/src/math/ec/rfc7748/X448.cs +++ b/crypto/src/math/ec/rfc7748/X448.cs @@ -2,10 +2,15 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Math.EC.Rfc7748 { public abstract class X448 { + public const int PointSize = 56; + public const int ScalarSize = 56; + private const uint C_A = 156326; private const uint C_A24 = (C_A + 2)/4; @@ -21,6 +26,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 private static uint[] precompBase = null; + public static bool CalculateAgreement(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff) + { + ScalarMult(k, kOff, u, uOff, r, rOff); + return !Arrays.AreAllZeroes(r, rOff, PointSize); + } + private static uint Decode32(byte[] bs, int off) { uint n = bs[off]; diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index ff4587cb2..f9ba1ff97 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Math.Raw; @@ -11,6 +12,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { public abstract class Ed25519 { + public enum Algorithm + { + Ed25519 = 0, + Ed25519ctx = 1, + Ed25519ph = 2, + } + private const long M28L = 0x0FFFFFFFL; private const long M32L = 0xFFFFFFFFL; @@ -18,11 +26,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private const int ScalarUints = 8; private const int ScalarBytes = ScalarUints * 4; + public static readonly int PrehashSize = 64; public static readonly int PublicKeySize = PointBytes; public static readonly int SecretKeySize = 32; public static readonly int SignatureSize = PointBytes + ScalarBytes; - //private static readonly byte[] Dom2Prefix = Strings.ToByteArray("SigEd25519 no Ed25519 collisions"); + private static readonly byte[] Dom2Prefix = Strings.ToByteArray("SigEd25519 no Ed25519 collisions"); private static readonly uint[] P = { 0xFFFFFFEDU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0x7FFFFFFFU }; private static readonly uint[] L = { 0x5CF5D3EDU, 0x5812631AU, 0xA2F79CD6U, 0x14DEF9DEU, 0x00000000U, 0x00000000U, 0x00000000U, 0x10000000U }; @@ -96,6 +105,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ReduceScalar(result); } + private static bool CheckContextVar(byte[] ctx, byte phflag) + { + return ctx == null && phflag == 0x00 + || ctx != null && ctx.Length < 256; + } + private static bool CheckPointVar(byte[] p) { uint[] t = new uint[8]; @@ -111,6 +126,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return !Nat256.Gte(n, L); } + private static IDigest CreateDigest() + { + return new Sha512Digest(); + } + + public static IDigest CreatePrehash() + { + return CreateDigest(); + } + private static uint Decode24(byte[] bs, int off) { uint n = bs[off]; @@ -140,9 +165,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { byte[] py = Arrays.CopyOfRange(p, pOff, pOff + PointBytes); if (!CheckPointVar(py)) - { return false; - } int x_0 = (py[PointBytes - 1] & 0x80) >> 7; py[PointBytes - 1] &= 0x7F; @@ -158,15 +181,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 X25519Field.AddOne(v); if (!X25519Field.SqrtRatioVar(u, v, r.x)) - { return false; - } X25519Field.Normalize(r.x); if (x_0 == 1 && X25519Field.IsZeroVar(r.x)) - { return false; - } if (negate ^ (x_0 != (r.x[0] & 1))) { @@ -182,6 +201,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Decode32(k, kOff, n, 0, ScalarUints); } + private static void Dom2(IDigest d, byte phflag, byte[] ctx) + { + if (ctx != null) + { + d.BlockUpdate(Dom2Prefix, 0, Dom2Prefix.Length); + d.Update(phflag); + d.Update((byte)ctx.Length); + d.BlockUpdate(ctx, 0, ctx.Length); + } + } + private static void Encode24(uint n, byte[] bs, int off) { bs[off] = (byte)(n); @@ -220,7 +250,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { - Sha512Digest d = new Sha512Digest(); + IDigest d = CreateDigest(); byte[] h = new byte[d.GetDigestSize()]; d.BlockUpdate(sk, skOff, SecretKeySize); @@ -286,8 +316,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ws; } - private static void ImplSign(Sha512Digest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + private static void ImplSign(IDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { + Dom2(d, phflag, ctx); d.BlockUpdate(h, ScalarBytes, ScalarBytes); d.BlockUpdate(m, mOff, mLen); d.DoFinal(h, 0); @@ -296,8 +328,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 byte[] R = new byte[PointBytes]; ScalarMultBaseEncoded(r, R, 0); + Dom2(d, phflag, ctx); d.BlockUpdate(R, 0, PointBytes); - d.BlockUpdate(pk, 0, PointBytes); + d.BlockUpdate(pk, pkOff, PointBytes); d.BlockUpdate(m, mOff, mLen); d.DoFinal(h, 0); @@ -308,6 +341,90 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Array.Copy(S, 0, sig, sigOff + PointBytes, ScalarBytes); } + private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, + byte[] sig, int sigOff) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + + IDigest d = CreateDigest(); + byte[] h = new byte[d.GetDigestSize()]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + byte[] pk = new byte[PointBytes]; + ScalarMultBaseEncoded(s, pk, 0); + + ImplSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + private static void ImplSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + + IDigest d = CreateDigest(); + byte[] h = new byte[d.GetDigestSize()]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + ImplSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + + byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); + byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + + if (!CheckPointVar(R)) + return false; + + if (!CheckScalarVar(S)) + return false; + + PointExt pA = new PointExt(); + if (!DecodePointVar(pk, pkOff, true, pA)) + return false; + + IDigest d = CreateDigest(); + byte[] h = new byte[d.GetDigestSize()]; + + Dom2(d, phflag, ctx); + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(pk, pkOff, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.DoFinal(h, 0); + + byte[] k = ReduceScalar(h); + + uint[] nS = new uint[ScalarUints]; + DecodeScalar(S, 0, nS); + + uint[] nA = new uint[ScalarUints]; + DecodeScalar(k, 0, nA); + + PointAccum pR = new PointAccum(); + ScalarMultStraussVar(nS, nA, pA, pR); + + byte[] check = new byte[PointBytes]; + EncodePoint(pR, check, 0); + + return Arrays.AreEqual(check, R); + } + private static void PointAddVar(bool negate, PointExt p, PointAccum r) { int[] A = X25519Field.Create(); @@ -518,9 +635,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Precompute() { if (precompBase != null) - { return; - } // Precomputed table for the base point in verification ladder { @@ -555,9 +670,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 ds[t] = PointCopy(p); - for (int s = 1; s < PrecompSpacing; ++s) + if (b + t != PrecompBlocks + PrecompTeeth - 2) { - PointDouble(p); + for (int s = 1; s < PrecompSpacing; ++s) + { + PointDouble(p); + } } } @@ -795,9 +913,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if ((cOff -= PrecompTeeth) < 0) - { break; - } PointDouble(r); } @@ -850,9 +966,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if (--bit < 0) - { break; - } PointDouble(r); } @@ -860,78 +974,101 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Sign(byte[] sk, int skOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - Sha512Digest d = new Sha512Digest(); - byte[] h = new byte[d.GetDigestSize()]; + byte[] ctx = null; + byte phflag = 0x00; - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0); + ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + byte[] ctx = null; + byte phflag = 0x00; - byte[] pk = new byte[PointBytes]; - ScalarMultBaseEncoded(s, pk, 0); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + byte phflag = 0x00; - ImplSign(d, h, s, pk, 0, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } - public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - Sha512Digest d = new Sha512Digest(); - byte[] h = new byte[d.GetDigestSize()]; + byte phflag = 0x00; - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) + { + byte phflag = 0x01; - ImplSign(d, h, s, pk, pkOff, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); } - public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) { - byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); - byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + byte phflag = 0x01; - if (!CheckPointVar(R)) - { - return false; - } - if (!CheckScalarVar(S)) - { - return false; - } + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); + } - PointExt pA = new PointExt(); - if (!DecodePointVar(pk, pkOff, true, pA)) - { - return false; - } + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); - Sha512Digest d = new Sha512Digest(); - byte[] h = new byte[d.GetDigestSize()]; + byte phflag = 0x01; - d.BlockUpdate(R, 0, PointBytes); - d.BlockUpdate(pk, pkOff, PointBytes); - d.BlockUpdate(m, mOff, mLen); - d.DoFinal(h, 0); + ImplSign(sk, skOff, ctx, phflag, m, 0, m.Length, sig, sigOff); + } - byte[] k = ReduceScalar(h); + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); - uint[] nS = new uint[ScalarUints]; - DecodeScalar(S, 0, nS); + byte phflag = 0x01; - uint[] nA = new uint[ScalarUints]; - DecodeScalar(k, 0, nA); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.Length, sig, sigOff); + } - PointAccum pR = new PointAccum(); - ScalarMultStraussVar(nS, nA, pA, pR); + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) + { + byte[] ctx = null; + byte phflag = 0x00; - byte[] check = new byte[PointBytes]; - EncodePoint(pR, check, 0); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); + } - return Arrays.AreEqual(check, R); + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + { + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); + } + + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) + { + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); + } + + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); + + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } } } diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index 52c215160..a1f0e93b0 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Math.Raw; @@ -11,6 +12,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { public abstract class Ed448 { + public enum Algorithm + { + Ed448 = 0, + Ed448ph = 1, + } + private const ulong M26UL = 0x03FFFFFFUL; private const ulong M28UL = 0x0FFFFFFFUL; @@ -18,6 +25,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private const int ScalarUints = 14; private const int ScalarBytes = ScalarUints * 4 + 1; + public static readonly int PrehashSize = 64; public static readonly int PublicKeySize = PointBytes; public static readonly int SecretKeySize = 57; public static readonly int SignatureSize = PointBytes + ScalarBytes; @@ -103,9 +111,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static bool CheckPointVar(byte[] p) { if ((p[PointBytes - 1] & 0x7F) != 0x00) - { return false; - } uint[] t = new uint[14]; Decode32(p, 0, t, 0, 14); @@ -115,15 +121,23 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static bool CheckScalarVar(byte[] s) { if (s[ScalarBytes - 1] != 0x00) - { return false; - } uint[] n = new uint[ScalarUints]; DecodeScalar(s, 0, n); return !Nat.Gte(ScalarUints, n, L); } + public static IXof CreatePrehash() + { + return CreateXof(); + } + + private static IXof CreateXof() + { + return new ShakeDigest(256); + } + private static uint Decode16(byte[] bs, int off) { uint n = bs[off]; @@ -160,9 +174,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { byte[] py = Arrays.CopyOfRange(p, pOff, pOff + PointBytes); if (!CheckPointVar(py)) - { return false; - } int x_0 = (py[PointBytes - 1] & 0x80) >> 7; py[PointBytes - 1] &= 0x7F; @@ -179,15 +191,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 X448Field.AddOne(v); if (!X448Field.SqrtRatioVar(u, v, r.x)) - { return false; - } X448Field.Normalize(r.x); if (x_0 == 1 && X448Field.IsZeroVar(r.x)) - { return false; - } if (negate ^ (x_0 != (r.x[0] & 1))) { @@ -205,7 +213,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Decode32(k, kOff, n, 0, ScalarUints); } - private static void Dom4(ShakeDigest d, byte x, byte[] y) + private static void Dom4(IXof d, byte x, byte[] y) { d.BlockUpdate(Dom4Prefix, 0, Dom4Prefix.Length); d.Update(x); @@ -251,7 +259,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { - ShakeDigest d = new ShakeDigest(256); + IXof d = CreateXof(); byte[] h = new byte[ScalarBytes * 2]; d.BlockUpdate(sk, skOff, SecretKeySize); @@ -317,10 +325,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ws; } - private static void ImplSign(ShakeDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + private static void ImplSign(IXof d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - byte phflag = 0x00; - Dom4(d, phflag, ctx); d.BlockUpdate(h, ScalarBytes, ScalarBytes); d.BlockUpdate(m, mOff, mLen); @@ -343,6 +350,90 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Array.Copy(S, 0, sig, sigOff + PointBytes, ScalarBytes); } + private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, + byte[] sig, int sigOff) + { + if (!CheckContextVar(ctx)) + throw new ArgumentException("ctx"); + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0, h.Length); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + byte[] pk = new byte[PointBytes]; + ScalarMultBaseEncoded(s, pk, 0); + + ImplSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + private static void ImplSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + if (!CheckContextVar(ctx)) + throw new ArgumentException("ctx"); + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0, h.Length); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + ImplSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx)) + throw new ArgumentException("ctx"); + + byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); + byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + + if (!CheckPointVar(R)) + return false; + + if (!CheckScalarVar(S)) + return false; + + PointExt pA = new PointExt(); + if (!DecodePointVar(pk, pkOff, true, pA)) + return false; + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + Dom4(d, phflag, ctx); + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(pk, pkOff, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.DoFinal(h, 0, h.Length); + + byte[] k = ReduceScalar(h); + + uint[] nS = new uint[ScalarUints]; + DecodeScalar(S, 0, nS); + + uint[] nA = new uint[ScalarUints]; + DecodeScalar(k, 0, nA); + + PointExt pR = new PointExt(); + ScalarMultStraussVar(nS, nA, pA, pR); + + byte[] check = new byte[PointBytes]; + EncodePoint(pR, check, 0); + + return Arrays.AreEqual(check, R); + } + private static void PointAddVar(bool negate, PointExt p, PointExt r) { uint[] A = X448Field.Create(); @@ -505,9 +596,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Precompute() { if (precompBase != null) - { return; - } PointExt p = new PointExt(); X448Field.Copy(B_x, 0, p.x, 0); @@ -533,9 +622,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 ds[t] = PointCopy(p); - for (int s = 1; s < PrecompSpacing; ++s) + if (b + t != PrecompBlocks + PrecompTeeth - 2) { - PointDouble(p); + for (int s = 1; s < PrecompSpacing; ++s) + { + PointDouble(p); + } } } @@ -888,8 +980,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 uint w = 0; for (int t = 0; t < PrecompTeeth; ++t) { - uint tBit = (n[tPos >> 5] >> (tPos & 0x1F)) & 1U; - w |= tBit << t; + uint tBit = n[tPos >> 5] >> (tPos & 0x1F); + w &= ~(1U << t); + w ^= (tBit << t); tPos += PrecompSpacing; } @@ -907,9 +1000,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if (--cOff < 0) - { break; - } PointDouble(r); } @@ -962,9 +1053,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if (--bit < 0) - { break; - } PointDouble(r); } @@ -972,96 +1061,77 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - if (!CheckContextVar(ctx)) - { - throw new ArgumentException("ctx"); - } + byte phflag = 0x00; - ShakeDigest d = new ShakeDigest(256); - byte[] h = new byte[ScalarBytes * 2]; + ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0, h.Length); + public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + byte phflag = 0x00; - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - byte[] pk = new byte[PointBytes]; - ScalarMultBaseEncoded(s, pk, 0); + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) + { + byte phflag = 0x01; - ImplSign(d, h, s, pk, 0, ctx, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); } - public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) { - if (!CheckContextVar(ctx)) - { - throw new ArgumentException("ctx"); - } + byte phflag = 0x01; - ShakeDigest d = new ShakeDigest(256); - byte[] h = new byte[ScalarBytes * 2]; + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); + } - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0, h.Length); + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IXof ph, byte[] sig, int sigOff) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + byte phflag = 0x01; - ImplSign(d, h, s, pk, pkOff, ctx, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, m, 0, m.Length, sig, sigOff); } - public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IXof ph, byte[] sig, int sigOff) { - if (!CheckContextVar(ctx)) - { - throw new ArgumentException("ctx"); - } - - byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); - byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); - if (!CheckPointVar(R)) - { - return false; - } - if (!CheckScalarVar(S)) - { - return false; - } + byte phflag = 0x01; - PointExt pA = new PointExt(); - if (!DecodePointVar(pk, pkOff, true, pA)) - { - return false; - } + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.Length, sig, sigOff); + } + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + { byte phflag = 0x00; - ShakeDigest d = new ShakeDigest(256); - byte[] h = new byte[ScalarBytes * 2]; - - Dom4(d, phflag, ctx); - d.BlockUpdate(R, 0, PointBytes); - d.BlockUpdate(pk, pkOff, PointBytes); - d.BlockUpdate(m, mOff, mLen); - d.DoFinal(h, 0, h.Length); - - byte[] k = ReduceScalar(h); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); + } - uint[] nS = new uint[ScalarUints]; - DecodeScalar(S, 0, nS); + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) + { + byte phflag = 0x01; - uint[] nA = new uint[ScalarUints]; - DecodeScalar(k, 0, nA); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); + } - PointExt pR = new PointExt(); - ScalarMultStraussVar(nS, nA, pA, pR); + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IXof ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); - byte[] check = new byte[PointBytes]; - EncodePoint(pR, check, 0); + byte phflag = 0x01; - return Arrays.AreEqual(check, R); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } } } diff --git a/crypto/src/math/raw/Nat.cs b/crypto/src/math/raw/Nat.cs index 7ca60278a..040ade74f 100644 --- a/crypto/src/math/raw/Nat.cs +++ b/crypto/src/math/raw/Nat.cs @@ -488,21 +488,31 @@ namespace Org.BouncyCastle.Math.Raw public static void Mul(int len, uint[] x, uint[] y, uint[] zz) { - zz[len] = (uint)MulWord(len, x[0], y, zz); + zz[len] = MulWord(len, x[0], y, zz); for (int i = 1; i < len; ++i) { - zz[i + len] = (uint)MulWordAddTo(len, x[i], y, 0, zz, i); + zz[i + len] = MulWordAddTo(len, x[i], y, 0, zz, i); } } public static void Mul(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) { - zz[zzOff + len] = (uint)MulWord(len, x[xOff], y, yOff, zz, zzOff); + zz[zzOff + len] = MulWord(len, x[xOff], y, yOff, zz, zzOff); for (int i = 1; i < len; ++i) { - zz[zzOff + i + len] = (uint)MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i); + zz[zzOff + i + len] = MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i); + } + } + + public static void Mul(uint[] x, int xOff, int xLen, uint[] y, int yOff, int yLen, uint[] zz, int zzOff) + { + zz[zzOff + yLen] = MulWord(yLen, x[xOff], y, yOff, zz, zzOff); + + for (int i = 1; i < xLen; ++i) + { + zz[zzOff + i + yLen] = MulWordAddTo(yLen, x[xOff + i], y, yOff, zz, zzOff + i); } } diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs index 156243f4e..1d3d75941 100644 --- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs +++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs @@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return result; } - public NotationData[] GetNotationDataOccurences() + public NotationData[] GetNotationDataOccurrences() { SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData); NotationData[] vals = new NotationData[notations.Length]; @@ -87,6 +87,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return vals; } + [Obsolete("Use 'GetNotationDataOccurrences' instead")] + public NotationData[] GetNotationDataOccurences() + { + return GetNotationDataOccurrences(); + } + public long GetIssuerKeyId() { SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId); diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs index a349a11d2..69eb3fa67 100644 --- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs +++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs @@ -2,6 +2,7 @@ using System; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Sec; @@ -22,59 +23,68 @@ namespace Org.BouncyCastle.Pkcs } public static PrivateKeyInfo CreatePrivateKeyInfo( - AsymmetricKeyParameter key) + AsymmetricKeyParameter privateKey) { - if (key == null) - throw new ArgumentNullException("key"); - if (!key.IsPrivate) - throw new ArgumentException("Public key passed - private key expected", "key"); + return CreatePrivateKeyInfo(privateKey, null); + } + + /** + * Create a PrivateKeyInfo representation of a private key with attributes. + * + * @param privateKey the key to be encoded into the info object. + * @param attributes the set of attributes to be included. + * @return the appropriate PrivateKeyInfo + * @throws java.io.IOException on an error encoding the key + */ + public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter privateKey, Asn1Set attributes) + { + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("Public key passed - private key expected", "privateKey"); - if (key is ElGamalPrivateKeyParameters) + if (privateKey is ElGamalPrivateKeyParameters) { - ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)key; + ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)privateKey; + ElGamalParameters egp = _key.Parameters; return new PrivateKeyInfo( - new AlgorithmIdentifier( - OiwObjectIdentifiers.ElGamalAlgorithm, - new ElGamalParameter( - _key.Parameters.P, - _key.Parameters.G).ToAsn1Object()), - new DerInteger(_key.X)); + new AlgorithmIdentifier(OiwObjectIdentifiers.ElGamalAlgorithm, new ElGamalParameter(egp.P, egp.G).ToAsn1Object()), + new DerInteger(_key.X), + attributes); } - if (key is DsaPrivateKeyParameters) + if (privateKey is DsaPrivateKeyParameters) { - DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)key; + DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)privateKey; + DsaParameters dp = _key.Parameters; return new PrivateKeyInfo( - new AlgorithmIdentifier( - X9ObjectIdentifiers.IdDsa, - new DsaParameter( - _key.Parameters.P, - _key.Parameters.Q, - _key.Parameters.G).ToAsn1Object()), - new DerInteger(_key.X)); + new AlgorithmIdentifier(X9ObjectIdentifiers.IdDsa, new DsaParameter(dp.P, dp.Q, dp.G).ToAsn1Object()), + new DerInteger(_key.X), + attributes); } - if (key is DHPrivateKeyParameters) + if (privateKey is DHPrivateKeyParameters) { - DHPrivateKeyParameters _key = (DHPrivateKeyParameters)key; + DHPrivateKeyParameters _key = (DHPrivateKeyParameters)privateKey; DHParameter p = new DHParameter( _key.Parameters.P, _key.Parameters.G, _key.Parameters.L); return new PrivateKeyInfo( new AlgorithmIdentifier(_key.AlgorithmOid, p.ToAsn1Object()), - new DerInteger(_key.X)); + new DerInteger(_key.X), + attributes); } - if (key is RsaKeyParameters) + if (privateKey is RsaKeyParameters) { AlgorithmIdentifier algID = new AlgorithmIdentifier( PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance); RsaPrivateKeyStructure keyStruct; - if (key is RsaPrivateCrtKeyParameters) + if (privateKey is RsaPrivateCrtKeyParameters) { - RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)key; + RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)privateKey; keyStruct = new RsaPrivateKeyStructure( _key.Modulus, @@ -88,7 +98,7 @@ namespace Org.BouncyCastle.Pkcs } else { - RsaKeyParameters _key = (RsaKeyParameters) key; + RsaKeyParameters _key = (RsaKeyParameters) privateKey; keyStruct = new RsaPrivateKeyStructure( _key.Modulus, @@ -101,12 +111,12 @@ namespace Org.BouncyCastle.Pkcs BigInteger.Zero); } - return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object()); + return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object(), attributes); } - if (key is ECPrivateKeyParameters) + if (privateKey is ECPrivateKeyParameters) { - ECPrivateKeyParameters priv = (ECPrivateKeyParameters)key; + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey; ECDomainParameters dp = priv.Parameters; int orderBitLength = dp.N.BitLength; @@ -145,12 +155,12 @@ namespace Org.BouncyCastle.Pkcs algID = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, x962); } - return new PrivateKeyInfo(algID, ec); + return new PrivateKeyInfo(algID, ec, attributes); } - if (key is Gost3410PrivateKeyParameters) + if (privateKey is Gost3410PrivateKeyParameters) { - Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)key; + Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)privateKey; if (_key.PublicKeyParamSet == null) throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); @@ -170,10 +180,42 @@ namespace Org.BouncyCastle.Pkcs CryptoProObjectIdentifiers.GostR3410x94, algParams.ToAsn1Object()); - return new PrivateKeyInfo(algID, new DerOctetString(keyBytes)); + return new PrivateKeyInfo(algID, new DerOctetString(keyBytes), attributes); + } + + if (privateKey is X448PrivateKeyParameters) + { + X448PrivateKeyParameters key = (X448PrivateKeyParameters)privateKey; + + return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), + new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded()); + } + + if (privateKey is X25519PrivateKeyParameters) + { + X25519PrivateKeyParameters key = (X25519PrivateKeyParameters)privateKey; + + return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), + new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded()); + } + + if (privateKey is Ed448PrivateKeyParameters) + { + Ed448PrivateKeyParameters key = (Ed448PrivateKeyParameters)privateKey; + + return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), + new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded()); + } + + if (privateKey is Ed25519PrivateKeyParameters) + { + Ed25519PrivateKeyParameters key = (Ed25519PrivateKeyParameters)privateKey; + + return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), + new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded()); } - throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(key)); + throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(privateKey)); } public static PrivateKeyInfo CreatePrivateKeyInfo( diff --git a/crypto/src/security/AgreementUtilities.cs b/crypto/src/security/AgreementUtilities.cs index 12d427c8c..26d1628cc 100644 --- a/crypto/src/security/AgreementUtilities.cs +++ b/crypto/src/security/AgreementUtilities.cs @@ -1,6 +1,7 @@ using System.Collections; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Agreement; @@ -27,7 +28,10 @@ namespace Org.BouncyCastle.Security algorithms[X9ObjectIdentifiers.DHSinglePassCofactorDHSha1KdfScheme.Id] = "ECCDHWITHSHA1KDF"; algorithms[X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id] = "ECDHWITHSHA1KDF"; algorithms[X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id] = "ECMQVWITHSHA1KDF"; - } + + algorithms[EdECObjectIdentifiers.id_X25519.Id] = "X25519"; + algorithms[EdECObjectIdentifiers.id_X448.Id] = "X448"; + } public static IBasicAgreement GetBasicAgreement( DerObjectIdentifier oid) @@ -38,15 +42,9 @@ namespace Org.BouncyCastle.Security public static IBasicAgreement GetBasicAgreement( string algorithm) { - string upper = Platform.ToUpperInvariant(algorithm); - string mechanism = (string) algorithms[upper]; - - if (mechanism == null) - { - mechanism = upper; - } + string mechanism = GetMechanism(algorithm); - if (mechanism == "DH" || mechanism == "DIFFIEHELLMAN") + if (mechanism == "DH" || mechanism == "DIFFIEHELLMAN") return new DHBasicAgreement(); if (mechanism == "ECDH") @@ -72,15 +70,9 @@ namespace Org.BouncyCastle.Security string agreeAlgorithm, string wrapAlgorithm) { - string upper = Platform.ToUpperInvariant(agreeAlgorithm); - string mechanism = (string) algorithms[upper]; + string mechanism = GetMechanism(agreeAlgorithm); - if (mechanism == null) - { - mechanism = upper; - } - - // 'DHWITHSHA1KDF' retained for backward compatibility + // 'DHWITHSHA1KDF' retained for backward compatibility if (mechanism == "DHWITHSHA1KDF" || mechanism == "ECDHWITHSHA1KDF") return new ECDHWithKdfBasicAgreement( wrapAlgorithm, @@ -96,10 +88,37 @@ namespace Org.BouncyCastle.Security throw new SecurityUtilityException("Basic Agreement (with KDF) " + agreeAlgorithm + " not recognised."); } + public static IRawAgreement GetRawAgreement( + DerObjectIdentifier oid) + { + return GetRawAgreement(oid.Id); + } + + public static IRawAgreement GetRawAgreement( + string algorithm) + { + string mechanism = GetMechanism(algorithm); + + if (mechanism == "X25519") + return new X25519Agreement(); + + if (mechanism == "X448") + return new X448Agreement(); + + throw new SecurityUtilityException("Raw Agreement " + algorithm + " not recognised."); + } + public static string GetAlgorithmName( DerObjectIdentifier oid) { - return (string) algorithms[oid.Id]; + return (string)algorithms[oid.Id]; } + + private static string GetMechanism(string algorithm) + { + string upper = Platform.ToUpperInvariant(algorithm); + string mechanism = (string)algorithms[upper]; + return mechanism == null ? upper : mechanism; + } } } diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs index de05bc9ef..eb10baec8 100644 --- a/crypto/src/security/CipherUtilities.cs +++ b/crypto/src/security/CipherUtilities.cs @@ -53,6 +53,7 @@ namespace Org.BouncyCastle.Security SEED, SERPENT, SKIPJACK, + SM4, TEA, THREEFISH_256, THREEFISH_512, @@ -433,6 +434,9 @@ namespace Org.BouncyCastle.Security case CipherAlgorithm.SKIPJACK: blockCipher = new SkipjackEngine(); break; + case CipherAlgorithm.SM4: + blockCipher = new SM4Engine(); + break; case CipherAlgorithm.TEA: blockCipher = new TeaEngine(); break; @@ -740,6 +744,7 @@ namespace Org.BouncyCastle.Security case CipherAlgorithm.SEED: return new SeedEngine(); case CipherAlgorithm.SERPENT: return new SerpentEngine(); case CipherAlgorithm.SKIPJACK: return new SkipjackEngine(); + case CipherAlgorithm.SM4: return new SM4Engine(); case CipherAlgorithm.TEA: return new TeaEngine(); case CipherAlgorithm.THREEFISH_256: return new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256); case CipherAlgorithm.THREEFISH_512: return new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512); diff --git a/crypto/src/security/GeneratorUtilities.cs b/crypto/src/security/GeneratorUtilities.cs index db1929c16..08281493a 100644 --- a/crypto/src/security/GeneratorUtilities.cs +++ b/crypto/src/security/GeneratorUtilities.cs @@ -2,6 +2,7 @@ using System.Collections; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Iana; using Org.BouncyCastle.Asn1.Kisa; using Org.BouncyCastle.Asn1.Nist; @@ -109,6 +110,7 @@ namespace Org.BouncyCastle.Security KisaObjectIdentifiers.IdSeedCbc); AddKgAlgorithm("SERPENT"); AddKgAlgorithm("SKIPJACK"); + AddKgAlgorithm("SM4"); AddKgAlgorithm("TEA"); AddKgAlgorithm("THREEFISH-256"); AddKgAlgorithm("THREEFISH-512"); @@ -180,18 +182,29 @@ namespace Org.BouncyCastle.Security AddKpgAlgorithm("ECGOST3410", "ECGOST-3410", "GOST-3410-2001"); + AddKpgAlgorithm("Ed25519", + "Ed25519ctx", + "Ed25519ph", + EdECObjectIdentifiers.id_Ed25519); + AddKpgAlgorithm("Ed448", + "Ed448ph", + EdECObjectIdentifiers.id_Ed448); AddKpgAlgorithm("ELGAMAL"); AddKpgAlgorithm("GOST3410", "GOST-3410", "GOST-3410-94"); AddKpgAlgorithm("RSA", "1.2.840.113549.1.1.1"); + AddKpgAlgorithm("X25519", + EdECObjectIdentifiers.id_X25519); + AddKpgAlgorithm("X448", + EdECObjectIdentifiers.id_X448); AddDefaultKeySizeEntries(64, "DES"); AddDefaultKeySizeEntries(80, "SKIPJACK"); AddDefaultKeySizeEntries(128, "AES128", "BLOWFISH", "CAMELLIA128", "CAST5", "DESEDE", "HC128", "HMACMD2", "HMACMD4", "HMACMD5", "HMACRIPEMD128", "IDEA", "NOEKEON", - "RC2", "RC4", "RC5", "SALSA20", "SEED", "TEA", "XTEA", "VMPC", "VMPC-KSA3"); + "RC2", "RC4", "RC5", "SALSA20", "SEED", "SM4", "TEA", "XTEA", "VMPC", "VMPC-KSA3"); AddDefaultKeySizeEntries(160, "HMACRIPEMD160", "HMACSHA1"); AddDefaultKeySizeEntries(192, "AES", "AES192", "CAMELLIA192", "DESEDE3", "HMACTIGER", "RIJNDAEL", "SERPENT", "TNEPRES"); @@ -216,11 +229,11 @@ namespace Org.BouncyCastle.Security string canonicalName, params object[] aliases) { - kgAlgorithms[canonicalName] = canonicalName; + kgAlgorithms[Platform.ToUpperInvariant(canonicalName)] = canonicalName; foreach (object alias in aliases) { - kgAlgorithms[alias.ToString()] = canonicalName; + kgAlgorithms[Platform.ToUpperInvariant(alias.ToString())] = canonicalName; } } @@ -228,11 +241,11 @@ namespace Org.BouncyCastle.Security string canonicalName, params object[] aliases) { - kpgAlgorithms[canonicalName] = canonicalName; + kpgAlgorithms[Platform.ToUpperInvariant(canonicalName)] = canonicalName; foreach (object alias in aliases) { - kpgAlgorithms[alias.ToString()] = canonicalName; + kpgAlgorithms[Platform.ToUpperInvariant(alias.ToString())] = canonicalName; } } @@ -248,7 +261,7 @@ namespace Org.BouncyCastle.Security foreach (object alias in aliases) { - kgAlgorithms[alias.ToString()] = mainName; + kgAlgorithms[Platform.ToUpperInvariant(alias.ToString())] = mainName; } } @@ -318,6 +331,12 @@ namespace Org.BouncyCastle.Security if (Platform.StartsWith(canonicalName, "EC")) return new ECKeyPairGenerator(canonicalName); + if (canonicalName == "Ed25519") + return new Ed25519KeyPairGenerator(); + + if (canonicalName == "Ed448") + return new Ed448KeyPairGenerator(); + if (canonicalName == "ELGAMAL") return new ElGamalKeyPairGenerator(); @@ -327,6 +346,12 @@ namespace Org.BouncyCastle.Security if (canonicalName == "RSA") return new RsaKeyPairGenerator(); + if (canonicalName == "X25519") + return new X25519KeyPairGenerator(); + + if (canonicalName == "X448") + return new X448KeyPairGenerator(); + throw new SecurityUtilityException("KeyPairGenerator " + algorithm + " (" + canonicalName + ") not supported."); } diff --git a/crypto/src/security/ParameterUtilities.cs b/crypto/src/security/ParameterUtilities.cs index c12155878..792067bba 100644 --- a/crypto/src/security/ParameterUtilities.cs +++ b/crypto/src/security/ParameterUtilities.cs @@ -103,6 +103,7 @@ namespace Org.BouncyCastle.Security KisaObjectIdentifiers.IdSeedCbc); AddAlgorithm("SERPENT"); AddAlgorithm("SKIPJACK"); + AddAlgorithm("SM4"); AddAlgorithm("TEA"); AddAlgorithm("THREEFISH-256"); AddAlgorithm("THREEFISH-512"); @@ -115,7 +116,8 @@ namespace Org.BouncyCastle.Security AddBasicIVSizeEntries(8, "BLOWFISH", "DES", "DESEDE", "DESEDE3"); AddBasicIVSizeEntries(16, "AES", "AES128", "AES192", "AES256", - "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "NOEKEON", "SEED"); + "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", + "NOEKEON", "SEED", "SM4"); // TODO These algorithms support an IV // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them diff --git a/crypto/src/security/PrivateKeyFactory.cs b/crypto/src/security/PrivateKeyFactory.cs index c9e19cc7d..0b07d0659 100644 --- a/crypto/src/security/PrivateKeyFactory.cs +++ b/crypto/src/security/PrivateKeyFactory.cs @@ -5,6 +5,7 @@ using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Sec; @@ -170,12 +171,37 @@ namespace Org.BouncyCastle.Security return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet); } + else if (algOid.Equals(EdECObjectIdentifiers.id_X25519)) + { + return new X25519PrivateKeyParameters(GetRawKey(keyInfo, X25519PrivateKeyParameters.KeySize), 0); + } + else if (algOid.Equals(EdECObjectIdentifiers.id_X448)) + { + return new X448PrivateKeyParameters(GetRawKey(keyInfo, X448PrivateKeyParameters.KeySize), 0); + } + else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519)) + { + return new Ed25519PrivateKeyParameters(GetRawKey(keyInfo, Ed25519PrivateKeyParameters.KeySize), 0); + } + else if (algOid.Equals(EdECObjectIdentifiers.id_Ed448)) + { + return new Ed448PrivateKeyParameters(GetRawKey(keyInfo, Ed448PrivateKeyParameters.KeySize), 0); + } else { - throw new SecurityUtilityException("algorithm identifier in key not recognised"); + throw new SecurityUtilityException("algorithm identifier in private key not recognised"); } } + private static byte[] GetRawKey(PrivateKeyInfo keyInfo, int expectedSize) + { + byte[] result = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets(); + if (expectedSize != result.Length) + throw new SecurityUtilityException("private key encoding has incorrect length"); + + return result; + } + public static AsymmetricKeyParameter DecryptKey( char[] passPhrase, EncryptedPrivateKeyInfo encInfo) diff --git a/crypto/src/security/PublicKeyFactory.cs b/crypto/src/security/PublicKeyFactory.cs index f1b28b774..e39748e45 100644 --- a/crypto/src/security/PublicKeyFactory.cs +++ b/crypto/src/security/PublicKeyFactory.cs @@ -5,6 +5,7 @@ using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Sec; @@ -218,12 +219,41 @@ namespace Org.BouncyCastle.Security return new Gost3410PublicKeyParameters(y, algParams.PublicKeyParamSet); } + else if (algOid.Equals(EdECObjectIdentifiers.id_X25519)) + { + return new X25519PublicKeyParameters(GetRawKey(keyInfo, X25519PublicKeyParameters.KeySize), 0); + } + else if (algOid.Equals(EdECObjectIdentifiers.id_X448)) + { + return new X448PublicKeyParameters(GetRawKey(keyInfo, X448PublicKeyParameters.KeySize), 0); + } + else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519)) + { + return new Ed25519PublicKeyParameters(GetRawKey(keyInfo, Ed25519PublicKeyParameters.KeySize), 0); + } + else if (algOid.Equals(EdECObjectIdentifiers.id_Ed448)) + { + return new Ed448PublicKeyParameters(GetRawKey(keyInfo, Ed448PublicKeyParameters.KeySize), 0); + } else { - throw new SecurityUtilityException("algorithm identifier in key not recognised: " + algOid); + throw new SecurityUtilityException("algorithm identifier in public key not recognised: " + algOid); } } + private static byte[] GetRawKey(SubjectPublicKeyInfo keyInfo, int expectedSize) + { + /* + * TODO[RFC 8422] + * - Require keyInfo.Algorithm.Parameters == null? + */ + byte[] result = keyInfo.PublicKeyData.GetOctets(); + if (expectedSize != result.Length) + throw new SecurityUtilityException("public key encoding has incorrect length"); + + return result; + } + private static bool IsPkcsDHParam(Asn1Sequence seq) { if (seq.Count == 2) diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs index 44281503a..a9045ae6e 100644 --- a/crypto/src/security/SignerUtilities.cs +++ b/crypto/src/security/SignerUtilities.cs @@ -4,6 +4,7 @@ using System.IO; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; @@ -231,7 +232,13 @@ namespace Org.BouncyCastle.Security algorithms["GOST3411WITHECGOST3410"] = "ECGOST3410"; algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001.Id] = "ECGOST3410"; - + algorithms["ED25519"] = "Ed25519"; + algorithms[EdECObjectIdentifiers.id_Ed25519.Id] = "Ed25519"; + algorithms["ED25519CTX"] = "Ed25519ctx"; + algorithms["ED25519PH"] = "Ed25519ph"; + algorithms["ED448"] = "Ed448"; + algorithms[EdECObjectIdentifiers.id_Ed448.Id] = "Ed448"; + algorithms["ED448PH"] = "Ed448ph"; oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption; oids["MD4withRSA"] = PkcsObjectIdentifiers.MD4WithRsaEncryption; @@ -264,6 +271,9 @@ namespace Org.BouncyCastle.Security oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + + oids["Ed25519"] = EdECObjectIdentifiers.id_Ed25519; + oids["Ed448"] = EdECObjectIdentifiers.id_Ed448; } /// <summary> @@ -361,6 +371,30 @@ namespace Org.BouncyCastle.Security if (mechanism == null) mechanism = algorithm; + if (Platform.StartsWith(mechanism, "Ed")) + { + if (mechanism.Equals("Ed25519")) + { + return new Ed25519Signer(); + } + if (mechanism.Equals("Ed25519ctx")) + { + return new Ed25519ctxSigner(Arrays.EmptyBytes); + } + if (mechanism.Equals("Ed25519ph")) + { + return new Ed25519phSigner(Arrays.EmptyBytes); + } + if (mechanism.Equals("Ed448")) + { + return new Ed448Signer(Arrays.EmptyBytes); + } + if (mechanism.Equals("Ed448ph")) + { + return new Ed448phSigner(Arrays.EmptyBytes); + } + } + if (mechanism.Equals("RSA")) { return (new RsaDigestSigner(new NullDigest(), (AlgorithmIdentifier)null)); diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index df9b4e7ee..a9a574dbf 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -8,6 +8,19 @@ namespace Org.BouncyCastle.Utilities /// <summary> General array utilities.</summary> public abstract class Arrays { + public static readonly byte[] EmptyBytes = new byte[0]; + public static readonly int[] EmptyInts = new int[0]; + + public static bool AreAllZeroes(byte[] buf, int off, int len) + { + uint bits = 0; + for (int i = 0; i < len; ++i) + { + bits |= buf[off + i]; + } + return bits == 0; + } + public static bool AreEqual( bool[] a, bool[] b) diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs index ccbf872c4..e746b0ef4 100644 --- a/crypto/src/util/Integers.cs +++ b/crypto/src/util/Integers.cs @@ -9,9 +9,21 @@ namespace Org.BouncyCastle.Utilities return (i << distance) ^ (int)((uint)i >> -distance); } + [CLSCompliantAttribute(false)] + public static uint RotateLeft(uint i, int distance) + { + return (i << distance) ^ (i >> -distance); + } + public static int RotateRight(int i, int distance) { return (int)((uint)i >> distance) ^ (i << -distance); } + + [CLSCompliantAttribute(false)] + public static uint RotateRight(uint i, int distance) + { + return (i >> distance) ^ (i << -distance); + } } } diff --git a/crypto/src/util/collections/CollectionUtilities.cs b/crypto/src/util/collections/CollectionUtilities.cs index 18fcb6774..cac158226 100644 --- a/crypto/src/util/collections/CollectionUtilities.cs +++ b/crypto/src/util/collections/CollectionUtilities.cs @@ -39,6 +39,14 @@ namespace Org.BouncyCastle.Utilities.Collections return new UnmodifiableSetProxy(s); } + public static object RequireNext(IEnumerator e) + { + if (!e.MoveNext()) + throw new InvalidOperationException(); + + return e.Current; + } + public static string ToString(IEnumerable c) { StringBuilder sb = new StringBuilder("["); diff --git a/crypto/src/util/io/Streams.cs b/crypto/src/util/io/Streams.cs index cc7fa924c..503a1b4f1 100644 --- a/crypto/src/util/io/Streams.cs +++ b/crypto/src/util/io/Streams.cs @@ -96,5 +96,30 @@ namespace Org.BouncyCastle.Utilities.IO { buf.WriteTo(output); } + + /// <exception cref="IOException"></exception> + public static int WriteBufTo(MemoryStream buf, byte[] output, int offset) + { +#if PORTABLE + byte[] bytes = buf.ToArray(); + bytes.CopyTo(output, offset); + return bytes.Length; +#else + int size = (int)buf.Length; + buf.WriteTo(new MemoryStream(output, offset, size, true)); + return size; +#endif + } + + public static void WriteZeroes(Stream outStr, long count) + { + byte[] zeroes = new byte[BufferSize]; + while (count > BufferSize) + { + outStr.Write(zeroes, 0, BufferSize); + count -= BufferSize; + } + outStr.Write(zeroes, 0, (int)count); + } } } diff --git a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs index 7614321d4..fca5da3f5 100644 --- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs @@ -3,6 +3,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; @@ -26,20 +27,20 @@ namespace Org.BouncyCastle.X509 /// <summary> /// Create a Subject Public Key Info object for a given public key. /// </summary> - /// <param name="key">One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters</param> + /// <param name="publicKey">One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters</param> /// <returns>A subject public key info object.</returns> /// <exception cref="Exception">Throw exception if object provided is not one of the above.</exception> public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo( - AsymmetricKeyParameter key) + AsymmetricKeyParameter publicKey) { - if (key == null) - throw new ArgumentNullException("key"); - if (key.IsPrivate) - throw new ArgumentException("Private key passed - public key expected.", "key"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (publicKey.IsPrivate) + throw new ArgumentException("Private key passed - public key expected.", "publicKey"); - if (key is ElGamalPublicKeyParameters) + if (publicKey is ElGamalPublicKeyParameters) { - ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key; + ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)publicKey; ElGamalParameters kp = _key.Parameters; SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( @@ -51,9 +52,9 @@ namespace Org.BouncyCastle.X509 return info; } - if (key is DsaPublicKeyParameters) + if (publicKey is DsaPublicKeyParameters) { - DsaPublicKeyParameters _key = (DsaPublicKeyParameters) key; + DsaPublicKeyParameters _key = (DsaPublicKeyParameters) publicKey; DsaParameters kp = _key.Parameters; Asn1Encodable ae = kp == null ? null @@ -64,9 +65,9 @@ namespace Org.BouncyCastle.X509 new DerInteger(_key.Y)); } - if (key is DHPublicKeyParameters) + if (publicKey is DHPublicKeyParameters) { - DHPublicKeyParameters _key = (DHPublicKeyParameters) key; + DHPublicKeyParameters _key = (DHPublicKeyParameters) publicKey; DHParameters kp = _key.Parameters; SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( @@ -78,9 +79,9 @@ namespace Org.BouncyCastle.X509 return info; } // End of DH - if (key is RsaKeyParameters) + if (publicKey is RsaKeyParameters) { - RsaKeyParameters _key = (RsaKeyParameters) key; + RsaKeyParameters _key = (RsaKeyParameters) publicKey; SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance), @@ -89,9 +90,9 @@ namespace Org.BouncyCastle.X509 return info; } // End of RSA. - if (key is ECPublicKeyParameters) + if (publicKey is ECPublicKeyParameters) { - ECPublicKeyParameters _key = (ECPublicKeyParameters) key; + ECPublicKeyParameters _key = (ECPublicKeyParameters) publicKey; if (_key.AlgorithmName == "ECGOST3410") { @@ -139,9 +140,9 @@ namespace Org.BouncyCastle.X509 } } // End of EC - if (key is Gost3410PublicKeyParameters) + if (publicKey is Gost3410PublicKeyParameters) { - Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) key; + Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) publicKey; if (_key.PublicKeyParamSet == null) throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); @@ -164,7 +165,35 @@ namespace Org.BouncyCastle.X509 return new SubjectPublicKeyInfo(algID, new DerOctetString(keyBytes)); } - throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(key)); + if (publicKey is X448PublicKeyParameters) + { + X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.GetEncoded()); + } + + if (publicKey is X25519PublicKeyParameters) + { + X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.GetEncoded()); + } + + if (publicKey is Ed448PublicKeyParameters) + { + Ed448PublicKeyParameters key = (Ed448PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), key.GetEncoded()); + } + + if (publicKey is Ed25519PublicKeyParameters) + { + Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.GetEncoded()); + } + + throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(publicKey)); } private static void ExtractBytes( |