diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index 592dcc977a..b9d30e2d89 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -1057,6 +1057,7 @@ + @@ -1486,7 +1487,8 @@ - + + diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 26db97e1b9..709f383567 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -1051,6 +1051,7 @@ + @@ -1480,7 +1481,8 @@ - + + diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index 17fe3dda53..d6b2fc7461 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -1052,6 +1052,7 @@ + @@ -1481,7 +1482,8 @@ - + + diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 1ba78a83c4..95e0a3b3c3 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -5143,6 +5143,11 @@ SubType = "Code" BuildAction = "Compile" /> + + diff --git a/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs b/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs index b322ef233b..6688ac1786 100644 --- a/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs +++ b/crypto/src/asn1/gnu/GNUObjectIdentifiers.cs @@ -28,9 +28,9 @@ public abstract class GnuObjectIdentifiers public static readonly DerObjectIdentifier Crc = new DerObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms public static readonly DerObjectIdentifier Crc32 = new DerObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32 - /** 1.3.6.1.4.1.11591.15 - ellipticCurve */ - public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.6.1.4.1.11591.15"); + /** 1.3.6.1.4.1.11591.15 - ellipticCurve */ + public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.6.1.4.1.11591.15"); - public static readonly DerObjectIdentifier Ed25519 = EllipticCurve.Branch("1"); + public static readonly DerObjectIdentifier Ed25519 = EllipticCurve.Branch("1"); } } diff --git a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs index 1f101882f3..0ca5f289b6 100644 --- a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs +++ b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs @@ -74,6 +74,9 @@ public abstract class MiscObjectIdentifiers public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_CFB = cryptlib_algorithm.Branch("1.3"); public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_OFB = cryptlib_algorithm.Branch("1.4"); + // OpenPGP (draft-ietf-openpgp-rfc4880bis-10) + public static readonly DerObjectIdentifier Curve25519 = cryptlib_algorithm.Branch("5.1"); + // // Blake2b // diff --git a/crypto/src/bcpg/ECDHPublicBCPGKey.cs b/crypto/src/bcpg/ECDHPublicBCPGKey.cs index dc225e31e1..5c6785d261 100644 --- a/crypto/src/bcpg/ECDHPublicBCPGKey.cs +++ b/crypto/src/bcpg/ECDHPublicBCPGKey.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; namespace Org.BouncyCastle.Bcpg @@ -48,6 +49,21 @@ public ECDHPublicBcpgKey( VerifySymmetricKeyAlgorithm(); } + public ECDHPublicBcpgKey( + DerObjectIdentifier oid, + BigInteger encodedPoint, + HashAlgorithmTag hashAlgorithm, + SymmetricKeyAlgorithmTag symmetricKeyAlgorithm) + : base(oid, encodedPoint) + { + reserved = 1; + hashFunctionId = hashAlgorithm; + symAlgorithmId = symmetricKeyAlgorithm; + + VerifyHashAlgorithm(); + VerifySymmetricKeyAlgorithm(); + } + public virtual byte Reserved { get { return reserved; } diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs index bbed941dc7..f3b07ecdd0 100644 --- a/crypto/src/bcpg/PublicKeyPacket.cs +++ b/crypto/src/bcpg/PublicKeyPacket.cs @@ -48,6 +48,7 @@ internal PublicKeyPacket( key = new ECDHPublicBcpgKey(bcpgIn); break; case PublicKeyAlgorithmTag.ECDsa: + case PublicKeyAlgorithmTag.EdDsa: key = new ECDsaPublicBcpgKey(bcpgIn); break; default: diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs index 70138d584f..fb236dc533 100644 --- a/crypto/src/bcpg/SignaturePacket.cs +++ b/crypto/src/bcpg/SignaturePacket.cs @@ -147,6 +147,7 @@ internal SignaturePacket( signature = new MPInteger[]{ p, g, y }; break; case PublicKeyAlgorithmTag.ECDsa: + case PublicKeyAlgorithmTag.EdDsa: MPInteger ecR = new MPInteger(bcpgIn); MPInteger ecS = new MPInteger(bcpgIn); signature = new MPInteger[]{ ecR, ecS }; diff --git a/crypto/src/crypto/ec/CustomNamedCurves.cs b/crypto/src/crypto/ec/CustomNamedCurves.cs index 0eee66a338..53d0cf979e 100644 --- a/crypto/src/crypto/ec/CustomNamedCurves.cs +++ b/crypto/src/crypto/ec/CustomNamedCurves.cs @@ -3,6 +3,8 @@ using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.GM; +using Org.BouncyCastle.Asn1.Gnu; +using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Math; @@ -789,7 +791,8 @@ private static void DefineCurveAlias(string name, DerObjectIdentifier oid) static CustomNamedCurves() { - DefineCurve("curve25519", Curve25519Holder.Instance); + DefineCurveWithOid("ed25519", GnuObjectIdentifiers.Ed25519, Curve25519Holder.Instance); + DefineCurveWithOid("curve25519", MiscObjectIdentifiers.Curve25519, Curve25519Holder.Instance); //DefineCurveWithOid("secp112r1", SecObjectIdentifiers.SecP112r1, SecP112R1Holder.Instance); //DefineCurveWithOid("secp112r2", SecObjectIdentifiers.SecP112r2, SecP112R2Holder.Instance); diff --git a/crypto/src/crypto/signers/EdDsa25519Signer.cs b/crypto/src/crypto/signers/EdDsa25519Signer.cs new file mode 100644 index 0000000000..8a8eb0c9fb --- /dev/null +++ b/crypto/src/crypto/signers/EdDsa25519Signer.cs @@ -0,0 +1,47 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class EdDsa22519Signer : IDsa + { + private Ed25519Signer signer; + + public virtual string AlgorithmName => "EDDSA"; + + public EdDsa22519Signer() + { + signer = new Ed25519Signer(); + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + signer.Init(forSigning, parameters); + } + + public virtual BigInteger[] GenerateSignature(byte[] message) + { + signer.BlockUpdate(message, 0, message.Length); + byte[] sigBytes = signer.GenerateSignature(); + byte[] rBytes = new byte[32]; + Array.Copy(sigBytes, rBytes, 32); + byte[] sBytes = new byte[sigBytes.Length - 32]; + Array.Copy(sigBytes, 32, sBytes, 0, sigBytes.Length - 32); + BigInteger r = new BigInteger(1, rBytes); + BigInteger s = new BigInteger(1, sBytes); + return new BigInteger[2] { r, s }; + } + + public virtual bool VerifySignature(byte[] message, BigInteger r, BigInteger s) + { + signer.BlockUpdate(message, 0, message.Length); + byte[] rBytes = r.ToByteArrayUnsigned(); + byte[] sBytes = s.ToByteArrayUnsigned(); + byte[] sigBytes = new byte[64]; + Array.Copy(rBytes, 0, sigBytes, 32 - rBytes.Length, rBytes.Length); + Array.Copy(sBytes, 0, sigBytes, 64 - sBytes.Length, sBytes.Length); + return signer.VerifySignature(sigBytes); + } + } +} diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs index 336baf00d7..bd8f14d524 100644 --- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs +++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; +using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.IO; @@ -10,6 +11,7 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; @@ -102,10 +104,12 @@ public override void AddSessionInfo( private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random) { + AsymmetricKeyParameter akp = pubKey.GetKey(); + if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH) { IBufferedCipher c; - switch (pubKey.Algorithm) + switch (pubKey.Algorithm) { case PublicKeyAlgorithmTag.RsaEncrypt: case PublicKeyAlgorithmTag.RsaGeneral: @@ -119,29 +123,49 @@ private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random) throw new PgpException("Can't use DSA for encryption."); case PublicKeyAlgorithmTag.ECDsa: throw new PgpException("Can't use ECDSA for encryption."); + case PublicKeyAlgorithmTag.EdDsa: + throw new PgpException("Can't use EdDSA for encryption."); default: throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm); } - AsymmetricKeyParameter akp = pubKey.GetKey(); - c.Init(true, new ParametersWithRandom(akp, random)); + c.Init(true, new ParametersWithRandom(akp, random)); return c.DoFinal(sessionInfo); } ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key; + KeyParameter key; + byte[] encodedPublicKey; - // Generate the ephemeral key pair - IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH"); - gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random)); + if (akp is X25519PublicKeyParameters) + { + X25519PublicKeyParameters pub = (X25519PublicKeyParameters)akp; + byte[] privateKey = new byte[X25519.PointSize]; + X25519.GeneratePrivateKey(random, privateKey); + byte[] sharedKey = new byte[32]; + X25519.CalculateAgreement(privateKey, 0, pub.GetEncoded(), 0, sharedKey, 0); + byte[] publicKey = new byte[X25519.PointSize + 1]; + publicKey[0] = 0x40; // compressed point + X25519.GeneratePublicKey(privateKey, 0, publicKey, 1); + encodedPublicKey = publicKey; + key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, sharedKey)); + } + else + { + // Generate the ephemeral key pair + IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH"); + gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random)); - AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair(); - ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private; - ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public; + AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair(); + ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private; + ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public; - ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey(); - ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize(); + ECPublicKeyParameters pub = (ECPublicKeyParameters)akp; + ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize(); - KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S)); + key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S)); + encodedPublicKey = ephPub.Q.GetEncoded(false); + } IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm); w.Init(true, new ParametersWithRandom(key, random)); @@ -149,7 +173,7 @@ private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random) byte[] paddedSessionData = PgpPad.PadSessionData(sessionInfo, sessionKeyObfuscation); byte[] C = w.Wrap(paddedSessionData, 0, paddedSessionData.Length); - byte[] VB = new MPInteger(new BigInteger(1, ephPub.Q.GetEncoded(false))).GetEncoded(); + byte[] VB = new MPInteger(new BigInteger(1, encodedPublicKey)).GetEncoded(); byte[] rv = new byte[VB.Length + 1 + C.Length]; diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs index 92422c4132..462f17e037 100644 --- a/crypto/src/openpgp/PgpPublicKey.cs +++ b/crypto/src/openpgp/PgpPublicKey.cs @@ -1,7 +1,8 @@ using System; using System.Collections; using System.IO; - +using Org.BouncyCastle.Asn1.Gnu; +using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; @@ -10,6 +11,8 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Rfc7748; +using Org.BouncyCastle.Math.EC.Rfc8032; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; @@ -177,11 +180,35 @@ public PgpPublicKey( { bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q); } + else if (algorithm == PublicKeyAlgorithmTag.EdDsa) + { + bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q); + } else { throw new PgpException("unknown EC algorithm"); } } + else if (pubKey is Ed25519PublicKeyParameters) + { + Ed25519PublicKeyParameters ecK = (Ed25519PublicKeyParameters)pubKey; + byte[] encodedPoint = new byte[Ed25519.PublicKeySize + 1]; + encodedPoint[0] = 0x40; + ecK.Encode(encodedPoint, 1); + bcpgKey = new ECDsaPublicBcpgKey(GnuObjectIdentifiers.Ed25519, new BigInteger(1, encodedPoint)); + } + else if (pubKey is X25519PublicKeyParameters) + { + X25519PublicKeyParameters ecK = (X25519PublicKeyParameters)pubKey; + byte[] encodedPoint = new byte[X25519.PointSize + 1]; + encodedPoint[0] = 0x40; + ecK.Encode(encodedPoint, 1); + bcpgKey = new ECDHPublicBcpgKey( + MiscObjectIdentifiers.Curve25519, + new BigInteger(1, encodedPoint), + HashAlgorithmTag.Sha256, + SymmetricKeyAlgorithmTag.Aes128); + } else if (pubKey is ElGamalPublicKeyParameters) { ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey; @@ -495,7 +522,15 @@ public AsymmetricKeyParameter GetKey() case PublicKeyAlgorithmTag.ECDsa: return GetECKey("ECDSA"); case PublicKeyAlgorithmTag.ECDH: - return GetECKey("ECDH"); + if (((ECPublicBcpgKey)publicPk.Key).CurveOid.Id.Equals(MiscObjectIdentifiers.Curve25519.Id)) + { + byte[] encodedPoint = ((ECPublicBcpgKey)publicPk.Key).EncodedPoint.ToByteArrayUnsigned(); + return new X25519PublicKeyParameters(encodedPoint, 1); + } + else + return GetECKey("ECDH"); + case PublicKeyAlgorithmTag.EdDsa: + return new Ed25519PublicKeyParameters(((ECPublicBcpgKey)publicPk.Key).EncodedPoint.ToByteArrayUnsigned(), 1); case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key; diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs index 04fe3ad37c..0c47af3c3e 100644 --- a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs +++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs @@ -8,8 +8,11 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Custom.Djb; +using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Bcpg.OpenPgp { @@ -210,12 +213,23 @@ private byte[] RecoverSessionData(PgpPrivateKey privKey) byte[] keyEnc = new byte[keyLen]; Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length); - ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc); + KeyParameter key; - ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key; - ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize(); - - KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S)); + if (privKey.Key is X25519PrivateKeyParameters x25519privKeyParams) + { + byte[] sharedKey = new byte[X25519.PointSize]; + byte[] privateKey = new byte[X25519.PointSize]; + x25519privKeyParams.Encode(privateKey, 0); + X25519.CalculateAgreement(privateKey, 0, pEnc, 1, sharedKey, 0); + key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, sharedKey)); + } + else + { + ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc); + ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key; + ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize(); + key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S)); + } IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm); w.Init(false, key); diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs index a3ffd4a4ad..134fdf87dc 100644 --- a/crypto/src/openpgp/PgpSecretKey.cs +++ b/crypto/src/openpgp/PgpSecretKey.cs @@ -1,9 +1,12 @@ using System; using System.Collections; +using System.Diagnostics; using System.IO; - +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.EC; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; @@ -53,9 +56,27 @@ internal PgpSecretKey( secKey = new DsaSecretBcpgKey(dsK.X); break; case PublicKeyAlgorithmTag.ECDH: + if (privKey.Key is X25519PrivateKeyParameters) + { + X25519PrivateKeyParameters x25519K = (X25519PrivateKeyParameters)privKey.Key; + byte[] secretKey = new byte[X25519PrivateKeyParameters.KeySize]; + x25519K.Encode(secretKey, 0); + Array.Reverse(secretKey); + secKey = new ECSecretBcpgKey(new BigInteger(1, secretKey)); + } + else + { + ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key; + secKey = new ECSecretBcpgKey(ecK.D); + } + break; case PublicKeyAlgorithmTag.ECDsa: - ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key; - secKey = new ECSecretBcpgKey(ecK.D); + ECPrivateKeyParameters ecdsaK = (ECPrivateKeyParameters)privKey.Key; + secKey = new ECSecretBcpgKey(ecdsaK.D); + break; + case PublicKeyAlgorithmTag.EdDsa: + Ed25519PrivateKeyParameters edK = (Ed25519PrivateKeyParameters)privKey.Key; + secKey = new ECSecretBcpgKey(new BigInteger(1, edK.GetEncoded())); break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: @@ -674,11 +695,21 @@ internal PgpPrivateKey DoExtractPrivateKey(byte[] rawPassPhrase, bool clearPassP privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams); break; case PublicKeyAlgorithmTag.ECDH: - privateKey = GetECKey("ECDH", bcpgIn); + if (((ECPublicBcpgKey)secret.PublicKeyPacket.Key).CurveOid.Id.Equals(MiscObjectIdentifiers.Curve25519.Id)) + { + var x = new ECSecretBcpgKey(bcpgIn).X.ToByteArrayUnsigned(); + Array.Reverse(x); + privateKey = new X25519PrivateKeyParameters(x, 0); + } + else + privateKey = GetECKey("ECDH", bcpgIn); break; case PublicKeyAlgorithmTag.ECDsa: privateKey = GetECKey("ECDSA", bcpgIn); break; + case PublicKeyAlgorithmTag.EdDsa: + privateKey = new Ed25519PrivateKeyParameters(new ECSecretBcpgKey(bcpgIn).X.ToByteArrayUnsigned(), 0); + break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key; @@ -1103,58 +1134,6 @@ public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[] return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false, pubKey); } - internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, PgpPublicKey pubKey) - { - SXprUtilities.SkipOpenParenthesis(inputStream); - - string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); - if (type.Equals("protected-private-key")) - { - SXprUtilities.SkipOpenParenthesis(inputStream); - - string curveName; - - string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); - if (keyType.Equals("ecc")) - { - SXprUtilities.SkipOpenParenthesis(inputStream); - - string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); - curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); - - SXprUtilities.SkipCloseParenthesis(inputStream); - } - else - { - throw new PgpException("no curve details found"); - } - - byte[] qVal; - - SXprUtilities.SkipOpenParenthesis(inputStream); - - type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); - if (type.Equals("q")) - { - qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); - } - else - { - throw new PgpException("no q value found"); - } - - SXprUtilities.SkipCloseParenthesis(inputStream); - - byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName); - // TODO: check SHA-1 hash. - - return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null, - new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), pubKey); - } - - throw new PgpException("unknown key type found"); - } - /// /// Parse a secret key from one of the GPG S expression keys. /// @@ -1164,7 +1143,7 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[ /// public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase) { - return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true); + return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true, null); } /// @@ -1175,7 +1154,7 @@ public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] pa /// public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[] passPhrase) { - return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true); + return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true, null); } /// @@ -1186,37 +1165,50 @@ public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[ /// public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[] rawPassPhrase) { - return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false); + return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false, null); } /// /// Parse a secret key from one of the GPG S expression keys. /// - internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase) + internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, PgpPublicKey pubKey) { - SXprUtilities.SkipOpenParenthesis(inputStream); + SXprReader reader = new SXprReader(inputStream); + + reader.SkipOpenParenthesis(); - string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + string type = reader.ReadString(); if (type.Equals("protected-private-key")) { - SXprUtilities.SkipOpenParenthesis(inputStream); + reader.SkipOpenParenthesis(); string curveName; + DerObjectIdentifier curveOid; - string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + string keyType = reader.ReadString(); if (keyType.Equals("ecc")) { - SXprUtilities.SkipOpenParenthesis(inputStream); + reader.SkipOpenParenthesis(); - string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); - curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + string curveID = reader.ReadString(); + curveName = reader.ReadString(); if (Platform.StartsWith(curveName, "NIST ")) { curveName = curveName.Substring("NIST ".Length); } - SXprUtilities.SkipCloseParenthesis(inputStream); + curveOid = ECNamedCurveTable.GetOid(curveName); + if (curveOid == null) + { + curveOid = CustomNamedCurves.GetOid(curveName); + } + if (curveOid == null) + { + throw new PgpException("unknown curve"); + } + + reader.SkipCloseParenthesis(); } else { @@ -1224,79 +1216,181 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[ } byte[] qVal; + string flags = null; - SXprUtilities.SkipOpenParenthesis(inputStream); + reader.SkipOpenParenthesis(); - type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + type = reader.ReadString(); + if (type == "flags") + { + // Skip over flags + flags = reader.ReadString(); + reader.SkipCloseParenthesis(); + reader.SkipOpenParenthesis(); + type = reader.ReadString(); + } if (type.Equals("q")) { - qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + qVal = reader.ReadBytes(); } else { throw new PgpException("no q value found"); } - PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow, - new ECDsaPublicBcpgKey(ECNamedCurveTable.GetOid(curveName), new BigInteger(1, qVal))); + if (pubKey == null) + { + PublicKeyPacket pubPacket = new PublicKeyPacket( + flags == "eddsa" ? PublicKeyAlgorithmTag.EdDsa : PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow, + new ECDsaPublicBcpgKey(curveOid, new BigInteger(1, qVal))); + pubKey = new PgpPublicKey(pubPacket); + } - SXprUtilities.SkipCloseParenthesis(inputStream); + reader.SkipCloseParenthesis(); - byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName); - // TODO: check SHA-1 hash. + byte[] dValue = GetDValue(reader, pubKey.PublicKeyPacket, rawPassPhrase, clearPassPhrase, curveName); - return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null, - new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), new PgpPublicKey(pubPacket)); + return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null, + new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), pubKey); } throw new PgpException("unknown key type found"); } - private static byte[] GetDValue(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, string curveName) + private static void WriteSExprPublicKey(SXprWriter writer, PublicKeyPacket pubPacket, string curveName, string protectedAt) + { + writer.StartList(); + switch (pubPacket.Algorithm) + { + case PublicKeyAlgorithmTag.ECDsa: + case PublicKeyAlgorithmTag.EdDsa: + writer.WriteString("ecc"); + writer.StartList(); + writer.WriteString("curve"); + writer.WriteString(curveName); + writer.EndList(); + if (pubPacket.Algorithm == PublicKeyAlgorithmTag.EdDsa) + { + writer.StartList(); + writer.WriteString("flags"); + writer.WriteString("eddsa"); + writer.EndList(); + } + writer.StartList(); + writer.WriteString("q"); + writer.WriteBytes(((ECDsaPublicBcpgKey)pubPacket.Key).EncodedPoint.ToByteArrayUnsigned()); + writer.EndList(); + break; + + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaSign: + case PublicKeyAlgorithmTag.RsaGeneral: + RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)pubPacket.Key; + writer.WriteString("rsa"); + writer.StartList(); + writer.WriteString("n"); + writer.WriteBytes(rsaK.Modulus.ToByteArrayUnsigned()); + writer.EndList(); + writer.StartList(); + writer.WriteString("e"); + writer.WriteBytes(rsaK.PublicExponent.ToByteArrayUnsigned()); + writer.EndList(); + break; + + // TODO: DSA, etc. + default: + throw new PgpException("unsupported algorithm in S expression"); + } + + if (protectedAt != null) + { + writer.StartList(); + writer.WriteString("protected-at"); + writer.WriteString(protectedAt); + writer.EndList(); + } + writer.EndList(); + } + + private static byte[] GetDValue(SXprReader reader, PublicKeyPacket publicKey, byte[] rawPassPhrase, bool clearPassPhrase, string curveName) { string type; - SXprUtilities.SkipOpenParenthesis(inputStream); + reader.SkipOpenParenthesis(); string protection; + string protectedAt = null; S2k s2k; byte[] iv; byte[] secKeyData; - type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + type = reader.ReadString(); if (type.Equals("protected")) { - protection = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); + protection = reader.ReadString(); + + reader.SkipOpenParenthesis(); - SXprUtilities.SkipOpenParenthesis(inputStream); + s2k = reader.ParseS2k(); - s2k = SXprUtilities.ParseS2k(inputStream); + iv = reader.ReadBytes(); - iv = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + reader.SkipCloseParenthesis(); - SXprUtilities.SkipCloseParenthesis(inputStream); + secKeyData = reader.ReadBytes(); - secKeyData = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte()); + reader.SkipCloseParenthesis(); + + reader.SkipOpenParenthesis(); + + if (reader.ReadString().Equals("protected-at")) + { + protectedAt = reader.ReadString(); + } } else { throw new PgpException("protected block not found"); } - // TODO: recognise other algorithms - KeyParameter key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase); + byte[] data; + KeyParameter key; - byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length); + switch (protection) + { + case "openpgp-s2k3-sha1-aes256-cbc": + case "openpgp-s2k3-sha1-aes-cbc": + SymmetricKeyAlgorithmTag symmAlg = + protection.Equals("openpgp-s2k3-sha1-aes256-cbc") ? SymmetricKeyAlgorithmTag.Aes256 : SymmetricKeyAlgorithmTag.Aes128; + key = PgpUtilities.DoMakeKeyFromPassPhrase(symmAlg, s2k, rawPassPhrase, clearPassPhrase); + data = RecoverKeyData(symmAlg, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length); + // TODO: check SHA-1 hash. + break; + + case "openpgp-s2k3-ocb-aes": + MemoryStream aad = new MemoryStream(); + WriteSExprPublicKey(new SXprWriter(aad), publicKey, curveName, protectedAt); + key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase); + IBufferedCipher c = CipherUtilities.GetCipher("AES/OCB"); + c.Init(false, new AeadParameters(key, 128, iv, aad.ToArray())); + data = c.DoFinal(secKeyData, 0, secKeyData.Length); + break; + + case "openpgp-native": + default: + throw new PgpException(protection + " key format is not supported yet"); + } // // parse the secret key S-expr // Stream keyIn = new MemoryStream(data, false); - SXprUtilities.SkipOpenParenthesis(keyIn); - SXprUtilities.SkipOpenParenthesis(keyIn); - SXprUtilities.SkipOpenParenthesis(keyIn); - String name = SXprUtilities.ReadString(keyIn, keyIn.ReadByte()); - return SXprUtilities.ReadBytes(keyIn, keyIn.ReadByte()); + reader = new SXprReader(keyIn); + reader.SkipOpenParenthesis(); + reader.SkipOpenParenthesis(); + reader.SkipOpenParenthesis(); + String name = reader.ReadString(); + return reader.ReadBytes(); } } } diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs index fd5ab62328..cc6b211d5d 100644 --- a/crypto/src/openpgp/PgpUtilities.cs +++ b/crypto/src/openpgp/PgpUtilities.cs @@ -95,7 +95,10 @@ public static string GetSignatureName( case PublicKeyAlgorithmTag.ECDsa: encAlg = "ECDSA"; break; - case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. + case PublicKeyAlgorithmTag.EdDsa: + encAlg = "EdDSA"; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. case PublicKeyAlgorithmTag.ElGamalGeneral: encAlg = "ElGamal"; break; diff --git a/crypto/src/openpgp/Rfc6637Utilities.cs b/crypto/src/openpgp/Rfc6637Utilities.cs index 5d992ec515..97a2baaf14 100644 --- a/crypto/src/openpgp/Rfc6637Utilities.cs +++ b/crypto/src/openpgp/Rfc6637Utilities.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Org.BouncyCastle.Asn1; @@ -70,12 +70,17 @@ public static int GetKeyLength(SymmetricKeyAlgorithmTag algID) public static byte[] CreateKey(PublicKeyPacket pubKeyData, ECPoint s) { byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData); - ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key; - return Kdf(ecKey.HashAlgorithm, s, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial); } + public static byte[] CreateKey(PublicKeyPacket pubKeyData, byte[] ZB) + { + byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData); + ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key; + return Kdf(ecKey.HashAlgorithm, ZB, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial); + } + // RFC 6637 - Section 8 // curve_OID_len = (byte)len(curve_OID); // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 @@ -118,10 +123,13 @@ public static byte[] CreateUserKeyingMaterial(PublicKeyPacket pubKeyData) // return oBits leftmost bits of MB. private static byte[] Kdf(HashAlgorithmTag digestAlg, ECPoint s, int keyLen, byte[] parameters) { - byte[] ZB = s.XCoord.GetEncoded(); + return Kdf(digestAlg, s.XCoord.GetEncoded(), keyLen, parameters); + } + private static byte[] Kdf(HashAlgorithmTag digestAlg, byte[] ZB, int keyLen, byte[] parameters) + { string digestName = PgpUtilities.GetDigestName(digestAlg); - IDigest digest = DigestUtilities.GetDigest(digestName); + IDigest digest = DigestUtilities.GetDigest(digestName); digest.Update(0x00); digest.Update(0x00); diff --git a/crypto/src/openpgp/SXprReader.cs b/crypto/src/openpgp/SXprReader.cs new file mode 100644 index 0000000000..7e6d15b341 --- /dev/null +++ b/crypto/src/openpgp/SXprReader.cs @@ -0,0 +1,258 @@ +using System; +using System.IO; +using System.Text; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /** + * Reader for S-expression keys. This class will move when it finds a better home! + *

+ * Format documented here: + * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master + * http://people.csail.mit.edu/rivest/Sexp.txt + *

+ */ + class SXprReader + { + Stream stream; + int peekedByte; + + public SXprReader(Stream stream) + { + this.stream = stream; + this.peekedByte = -1; + } + + private int ReadByte() + { + if (this.peekedByte > 0) + { + int pb = this.peekedByte; + this.peekedByte = 0; + return pb; + } + return stream.ReadByte(); + } + + private void UnreadByte(int pb) + { + this.peekedByte = pb; + } + + private int ReadLength() + { + int ch; + int len = 0; + + while ((ch = ReadByte()) >= 0 && ch >= '0' && ch <= '9') + { + len = len * 10 + (ch - '0'); + } + UnreadByte(ch); + + return len; + } + + public string ReadString() + { + SkipWhitespace(); + + int ch = ReadByte(); + if (ch >= '0' && ch <= '9') + { + UnreadByte(ch); + + int len = ReadLength(); + ch = ReadByte(); + if (ch == ':') + { + char[] chars = new char[len]; + + for (int i = 0; i != chars.Length; i++) + { + chars[i] = (char)ReadByte(); + } + + return new string(chars); + } + else if (ch == '"') + { + return ReadQuotedString(len); + } + throw new IOException("unsupported encoding"); + } + else if (ch == '"') + { + return ReadQuotedString(0); + } + else if (ch == '{' || ch == '|' || ch == '#') + { + // TODO: Unsupported encoding + throw new IOException("unsupported encoding"); + } + else + { + StringBuilder sb = new StringBuilder(); + while (IsTokenChar(ch)) + { + sb.Append((char)ch); + ch = (char)ReadByte(); + } + UnreadByte(ch); + return sb.ToString(); + } + } + + private string ReadQuotedString(int length) + { + StringBuilder sb = new StringBuilder(length); + int ch; + bool skipNewLine = false; + do + { + ch = ReadByte(); + if ((ch == '\n' || ch == '\r') && skipNewLine) + { + skipNewLine = false; + } + else if (ch == '\\') + { + ch = (char)ReadByte(); + switch (ch) + { + case 'b': sb.Append('\b'); break; + case 't': sb.Append('\t'); break; + case 'v': sb.Append('\v'); break; + case 'n': sb.Append('\n'); break; + case 'r': sb.Append('\r'); break; + case 'f': sb.Append('\f'); break; + case '"': sb.Append('"'); break; + case '\'': sb.Append('\''); break; + case '\r': + case '\n': + skipNewLine = true; + break; + default: + // TODO: Octal value, hexadecimal value + throw new IOException("unsupported encoding"); + } + } + else if (ch != '"' && ch >= 0) + { + skipNewLine = false; + sb.Append((char)ch); + } + } + while (ch != '"' && ch > 0); + return sb.ToString(); + } + + private static bool IsTokenChar(int ch) + { + return + (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '-' || ch == '.' || + ch == '/' || ch == '_' || + ch == ':' || ch == '*' || + ch == '+' || ch == '='; + } + + public byte[] ReadBytes() + { + SkipWhitespace(); + + int ch = ReadByte(); + if (ch >= '0' && ch <= '9') + { + UnreadByte(ch); + + int len = ReadLength(); + + if (ReadByte() != ':') + throw new IOException("unsupported encoding"); + + byte[] data = new byte[len]; + + Streams.ReadFully(stream, data); + + return data; + } + else if (ch == '#') + { + StringBuilder sb = new StringBuilder(); + do + { + ch = ReadByte(); + if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) + sb.Append((char)ch); + } + while (ch != '#' && ch >= 0); + return Hex.Decode(sb.ToString()); + } + + throw new IOException("unsupported encoding"); + } + + public S2k ParseS2k() + { + SkipOpenParenthesis(); + + string alg = ReadString(); + byte[] iv = ReadBytes(); + long iterationCount = Int64.Parse(ReadString()); + + SkipCloseParenthesis(); + + // we have to return the actual iteration count provided. + return new MyS2k(HashAlgorithmTag.Sha1, iv, iterationCount); + } + + public void SkipWhitespace() + { + int ch = ReadByte(); + while (ch == ' ' || ch == '\r' || ch == '\n') + { + ch = ReadByte(); + } + UnreadByte(ch); + } + + public void SkipOpenParenthesis() + { + SkipWhitespace(); + + int ch = ReadByte(); + if (ch != '(') + throw new IOException("unknown character encountered"); + } + + public void SkipCloseParenthesis() + { + SkipWhitespace(); + + int ch = ReadByte(); + if (ch != ')') + throw new IOException("unknown character encountered"); + } + + private class MyS2k : S2k + { + private readonly long mIterationCount64; + + internal MyS2k(HashAlgorithmTag algorithm, byte[] iv, long iterationCount64) + : base(algorithm, iv, (int)iterationCount64) + { + this.mIterationCount64 = iterationCount64; + } + + public override long IterationCount + { + get { return mIterationCount64; } + } + } + } +} diff --git a/crypto/src/openpgp/SXprUtilities.cs b/crypto/src/openpgp/SXprUtilities.cs deleted file mode 100644 index 68ff373a83..0000000000 --- a/crypto/src/openpgp/SXprUtilities.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.IO; - -using Org.BouncyCastle.Utilities.IO; - -namespace Org.BouncyCastle.Bcpg.OpenPgp -{ - /** - * Utility functions for looking a S-expression keys. This class will move when it finds a better home! - *

- * Format documented here: - * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master - *

- */ - public sealed class SXprUtilities - { - private SXprUtilities() - { - } - - private static int ReadLength(Stream input, int ch) - { - int len = ch - '0'; - - while ((ch = input.ReadByte()) >= 0 && ch != ':') - { - len = len * 10 + ch - '0'; - } - - return len; - } - - internal static string ReadString(Stream input, int ch) - { - int len = ReadLength(input, ch); - - char[] chars = new char[len]; - - for (int i = 0; i != chars.Length; i++) - { - chars[i] = (char)input.ReadByte(); - } - - return new string(chars); - } - - internal static byte[] ReadBytes(Stream input, int ch) - { - int len = ReadLength(input, ch); - - byte[] data = new byte[len]; - - Streams.ReadFully(input, data); - - return data; - } - - internal static S2k ParseS2k(Stream input) - { - SkipOpenParenthesis(input); - - string alg = ReadString(input, input.ReadByte()); - byte[] iv = ReadBytes(input, input.ReadByte()); - long iterationCount = Int64.Parse(ReadString(input, input.ReadByte())); - - SkipCloseParenthesis(input); - - // we have to return the actual iteration count provided. - return new MyS2k(HashAlgorithmTag.Sha1, iv, iterationCount); - } - - internal static void SkipOpenParenthesis(Stream input) - { - int ch = input.ReadByte(); - if (ch != '(') - throw new IOException("unknown character encountered"); - } - - internal static void SkipCloseParenthesis(Stream input) - { - int ch = input.ReadByte(); - if (ch != ')') - throw new IOException("unknown character encountered"); - } - - private class MyS2k : S2k - { - private readonly long mIterationCount64; - - internal MyS2k(HashAlgorithmTag algorithm, byte[] iv, long iterationCount64) - : base(algorithm, iv, (int)iterationCount64) - { - this.mIterationCount64 = iterationCount64; - } - - public override long IterationCount - { - get { return mIterationCount64; } - } - } - } -} diff --git a/crypto/src/openpgp/SXprWriter.cs b/crypto/src/openpgp/SXprWriter.cs new file mode 100644 index 0000000000..640c4b6d6e --- /dev/null +++ b/crypto/src/openpgp/SXprWriter.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /** + * Writer for S-expression keys + *

+ * Format documented here: + * http://people.csail.mit.edu/rivest/Sexp.txt + * + * Only canonical S expression format is used. + *

+ */ + class SXprWriter + { + Stream output; + + public SXprWriter(Stream output) + { + this.output = output; + } + + public void StartList() + { + output.WriteByte((byte)'('); + } + + public void EndList() + { + output.WriteByte((byte)')'); + } + + public void WriteString(string s) + { + byte[] stringBytes = Encoding.UTF8.GetBytes(s); + byte[] lengthBytes = Encoding.UTF8.GetBytes(stringBytes.Length + ":"); + output.Write(lengthBytes, 0, lengthBytes.Length); + output.Write(stringBytes, 0, stringBytes.Length); + } + + public void WriteBytes(byte[] b) + { + byte[] lengthBytes = Encoding.UTF8.GetBytes(b.Length + ":"); + output.Write(lengthBytes, 0, lengthBytes.Length); + output.Write(b, 0, b.Length); + } + } +} diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs index 8a289897e9..1f263d17fa 100644 --- a/crypto/src/security/SignerUtilities.cs +++ b/crypto/src/security/SignerUtilities.cs @@ -610,6 +610,13 @@ public static ISigner GetSigner( return new DsaDigestSigner(new ECDsaSigner(), digest); } + if (Platform.EndsWith(mechanism, "WITHEDDSA")) + { + string digestName = mechanism.Substring(0, mechanism.LastIndexOf("WITH")); + IDigest digest = DigestUtilities.GetDigest(digestName); + return new DsaDigestSigner(new EdDsa22519Signer(), digest); + } + if (Platform.EndsWith(mechanism, "withCVC-ECDSA") || Platform.EndsWith(mechanism, "withPLAIN-ECDSA")) { diff --git a/crypto/test/src/openpgp/test/PgpECDHTest.cs b/crypto/test/src/openpgp/test/PgpECDHTest.cs index 94ab2a755e..9c307cb353 100644 --- a/crypto/test/src/openpgp/test/PgpECDHTest.cs +++ b/crypto/test/src/openpgp/test/PgpECDHTest.cs @@ -4,13 +4,16 @@ using System.Text; using NUnit.Framework; - +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Gnu; +using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; using Org.BouncyCastle.Utilities.Test; namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests @@ -51,6 +54,40 @@ public class PgpECDHTest "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" + "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg=="); + private static readonly byte[] testX25519PubKey = + Base64.Decode( + "mDMEX9XwXhYJKwYBBAHaRw8BAQdAR5ZghmMHL8wldNlOkmbaiAOdyF5V5bgZdKq7" + + "L+yb4A20HEVDREggPHRlc3QuZWNkaEBleGFtcGxlLmNvbT6IkAQTFggAOBYhBGoy" + + "UrxNv7c3S2JjGzewWiN8tfzXBQJf1fBeAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B" + + "AheAAAoJEDewWiN8tfzX0ZMA/AhEvrIgu+29eMQeuHOwX1ZY/UssU5TdVROQzGTL" + + "n5cgAP9hIKtt/mZ112HiAHDuWk2JskdtsuopnrEccz4PSEkSDLg4BF/V8F4SCisG" + + "AQQBl1UBBQEBB0DLPhNt/6GHDbb7vZW/iMsbXTZpgJNQiT6QA/4EzgYQLwMBCAeI" + + "eAQYFggAIBYhBGoyUrxNv7c3S2JjGzewWiN8tfzXBQJf1fBeAhsMAAoJEDewWiN8" + + "tfzXU34BAKJJLDee+qJCmUI20sMy/YoKfWmMnH2RBBHmLV8FAJ7vAP0e2wGixEfs" + + "oPqe8fHmvjQGxSByOyQGn7yD+oq9nVzTAA=="); + + private static readonly byte[] testX25519PrivKey = + Base64.Decode( + "lIYEX9XwXhYJKwYBBAHaRw8BAQdAR5ZghmMHL8wldNlOkmbaiAOdyF5V5bgZdKq7" + + "L+yb4A3+BwMCMscozrXr93fOFmtxu/BJjEJrwRl20Jrv9lryfM+SF4UHgVMmJUpJ" + + "1RuTbSnM2KaqHwOgmdrvf2FJnpg1vMafBk1CmopqkRzzrbJ6xQhiPrQcRUNESCA8" + + "dGVzdC5lY2RoQGV4YW1wbGUuY29tPoiQBBMWCAA4FiEEajJSvE2/tzdLYmMbN7Ba" + + "I3y1/NcFAl/V8F4CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQN7BaI3y1" + + "/NfRkwD8CES+siC77b14xB64c7BfVlj9SyxTlN1VE5DMZMuflyAA/2Egq23+ZnXX" + + "YeIAcO5aTYmyR22y6imesRxzPg9ISRIMnIsEX9XwXhIKKwYBBAGXVQEFAQEHQMs+" + + "E23/oYcNtvu9lb+IyxtdNmmAk1CJPpAD/gTOBhAvAwEIB/4HAwJ7ShSBrUuUAM5r" + + "G4I/gJKo+eBmbNC4NM81eALAF1vcovZPsGsiZ8IgXT64XiC1bpeAoINn6vM4vVbi" + + "LqNKqu6ll3ZgQ4po6vCW9GkhuEMmiHgEGBYIACAWIQRqMlK8Tb+3N0tiYxs3sFoj" + + "fLX81wUCX9XwXgIbDAAKCRA3sFojfLX811N+AQCiSSw3nvqiQplCNtLDMv2KCn1p" + + "jJx9kQQR5i1fBQCe7wD9HtsBosRH7KD6nvHx5r40BsUgcjskBp+8g/qKvZ1c0wA="); + + private static readonly byte[] testX25519Message = + Base64.Decode( + "hF4DbDc2fNL0VcUSAQdAqdV0v1D4X9cuGrT7+oQBpMFnw1wdfAcxH9xdO00s2HUw" + + "qB+XkIRETH7yesynLOKajmYftMWZRyTnW2tJUc1w5NFPjPxcbvd2bYmqkY57uAFg" + + "0kcBKhFklH2LRbBNThtQr3jn2YEFbNnhiGfOpoHfCn0oFh5RbXDwm+P3Q3tksvpZ" + + "wEGe2VkxLLe7BWnv/sRINQ2YpuaYshe8hw=="); + private void Generate() { SecureRandom random = SecureRandom.GetInstance("SHA1PRNG"); @@ -105,6 +142,71 @@ private void Generate() PgpPrivateKey pgpPrivKey = secRing.GetSecretKey().ExtractPrivateKey(passPhrase); } + private void Generate25519() + { + SecureRandom random = SecureRandom.GetInstance("SHA1PRNG"); + + // + // Generate a master key + // + IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + keyGen.Init(new ECKeyGenerationParameters(GnuObjectIdentifiers.Ed25519, random)); + + AsymmetricCipherKeyPair kpSign = keyGen.GenerateKeyPair(); + + PgpKeyPair ecdsaKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.EdDsa, kpSign, DateTime.UtcNow); + + // + // Generate an encryption key + // + keyGen = GeneratorUtilities.GetKeyPairGenerator("X25519"); + keyGen.Init(new ECKeyGenerationParameters(MiscObjectIdentifiers.Curve25519, random)); + + AsymmetricCipherKeyPair kpEnc = keyGen.GenerateKeyPair(); + + PgpKeyPair ecdhKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.ECDH, kpEnc, DateTime.UtcNow); + + // + // Generate a key ring + // + char[] passPhrase = "test".ToCharArray(); + PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator(PgpSignature.PositiveCertification, ecdsaKeyPair, + "test@bouncycastle.org", SymmetricKeyAlgorithmTag.Aes256, passPhrase, true, null, null, random); + keyRingGen.AddSubKey(ecdhKeyPair); + + PgpPublicKeyRing pubRing = keyRingGen.GeneratePublicKeyRing(); + + // TODO: add check of KdfParameters + DoBasicKeyRingCheck(pubRing); + + PgpSecretKeyRing secRing = keyRingGen.GenerateSecretKeyRing(); + + PgpPublicKeyRing pubRingEnc = new PgpPublicKeyRing(pubRing.GetEncoded()); + if (!Arrays.AreEqual(pubRing.GetEncoded(), pubRingEnc.GetEncoded())) + { + Fail("public key ring encoding failed"); + } + + PgpSecretKeyRing secRingEnc = new PgpSecretKeyRing(secRing.GetEncoded()); + if (!Arrays.AreEqual(secRing.GetEncoded(), secRingEnc.GetEncoded())) + { + Fail("secret key ring encoding failed"); + } + + // Extract back the ECDH key and verify the encoded values to ensure correct endianness + PgpSecretKey pgpSecretKey = secRing.GetSecretKey(ecdhKeyPair.KeyId); + PgpPrivateKey pgpPrivKey = pgpSecretKey.ExtractPrivateKey(passPhrase); + + if (!Arrays.AreEqual(((X25519PrivateKeyParameters)kpEnc.Private).GetEncoded(), ((X25519PrivateKeyParameters)pgpPrivKey.Key).GetEncoded())) + { + Fail("private key round trip failed"); + } + if (!Arrays.AreEqual(((X25519PublicKeyParameters)kpEnc.Public).GetEncoded(), ((X25519PublicKeyParameters)pgpSecretKey.PublicKey.GetKey()).GetEncoded())) + { + Fail("private key round trip failed"); + } + } + private void TestDecrypt(PgpSecretKeyRing secretKeyRing) { PgpObjectFactory pgpF = new PgpObjectFactory(testMessage); @@ -134,14 +236,14 @@ private void TestDecrypt(PgpSecretKeyRing secretKeyRing) // } } - private void EncryptDecryptTest() + private void EncryptDecryptTest(string algorithm, DerObjectIdentifier curve) { SecureRandom random = SecureRandom.GetInstance("SHA1PRNG"); byte[] text = Encoding.ASCII.GetBytes("hello world!"); - IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator("ECDH"); - keyGen.Init(new ECKeyGenerationParameters(SecObjectIdentifiers.SecP256r1, random)); + IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator(algorithm); + keyGen.Init(new ECKeyGenerationParameters(curve, random)); AsymmetricCipherKeyPair kpEnc = keyGen.GenerateKeyPair(); @@ -197,6 +299,108 @@ private void EncryptDecryptTest() } } + + private void EncryptDecryptX25519KeysTest() + { + SecureRandom random = SecureRandom.GetInstance("SHA1PRNG"); + + /*IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator(algorithm); + keyGen.Init(new ECKeyGenerationParameters(curve, random)); + + AsymmetricCipherKeyPair kpEnc = keyGen.GenerateKeyPair(); + + PgpKeyPair ecdhKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.ECDH, kpEnc, DateTime.UtcNow);*/ + PgpPublicKeyRing publicKeyRing = new PgpPublicKeyRing(testX25519PubKey); + + PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing(testX25519PrivKey); + + PgpSecretKey secretKey = secretKeyRing.GetSecretKey(0x6c37367cd2f455c5); + + byte[] text = Encoding.ASCII.GetBytes("hello world!"); + + PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); + MemoryStream ldOut = new MemoryStream(); + Stream pOut = lData.Open(ldOut, PgpLiteralDataGenerator.Utf8, PgpLiteralData.Console, text.Length, DateTime.UtcNow); + + pOut.Write(text, 0, text.Length); + + pOut.Close(); + + byte[] data = ldOut.ToArray(); + + MemoryStream cbOut = new MemoryStream(); + + PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, random); + cPk.AddMethod(publicKeyRing.GetPublicKey(0x6c37367cd2f455c5)); + + Stream cOut = cPk.Open(new UncloseableStream(cbOut), data.Length); + + cOut.Write(data, 0, data.Length); + + cOut.Close(); + + PgpObjectFactory pgpF = new PgpObjectFactory(cbOut.ToArray()); + + PgpEncryptedDataList encList = (PgpEncryptedDataList)pgpF.NextPgpObject(); + + PgpPublicKeyEncryptedData encP = (PgpPublicKeyEncryptedData)encList[0]; + + Stream clear = encP.GetDataStream(secretKey.ExtractPrivateKey("test".ToCharArray())); + + pgpF = new PgpObjectFactory(clear); + + PgpLiteralData ld = (PgpLiteralData)pgpF.NextPgpObject(); + + clear = ld.GetInputStream(); + MemoryStream bOut = new MemoryStream(); + + int ch; + while ((ch = clear.ReadByte()) >= 0) + { + bOut.WriteByte((byte)ch); + } + + byte[] output = bOut.ToArray(); + + if (!AreEqual(output, text)) + { + Fail("wrong plain text in Generated packet"); + } + } + + private void GnuPGCrossCheck() + { + PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing(testX25519PrivKey); + + PgpObjectFactory pgpF = new PgpObjectFactory(testX25519Message); + + PgpEncryptedDataList encList = (PgpEncryptedDataList)pgpF.NextPgpObject(); + + PgpPublicKeyEncryptedData encP = (PgpPublicKeyEncryptedData)encList[0]; + + PgpSecretKey secretKey = secretKeyRing.GetSecretKey(0x6c37367cd2f455c5); + + PgpPrivateKey pgpPrivKey = secretKey.ExtractPrivateKey("test".ToCharArray()); + + Stream clear = encP.GetDataStream(pgpPrivKey); + + pgpF = new PgpObjectFactory(clear); + + PgpCompressedData c1 = (PgpCompressedData)pgpF.NextPgpObject(); + + pgpF = new PgpObjectFactory(c1.GetDataStream()); + + PgpLiteralData ld = (PgpLiteralData)pgpF.NextPgpObject(); + + Stream inLd = ld.GetDataStream(); + byte[] bytes = Streams.ReadAll(inLd); + + if (!Arrays.AreEqual(bytes, Encoding.ASCII.GetBytes("hello world!"))) + { + Fail("wrong plain text in decrypted packet"); + } + } + public override void PerformTest() { // @@ -213,9 +417,17 @@ public override void PerformTest() TestDecrypt(secretKeyRing); - EncryptDecryptTest(); + EncryptDecryptTest("ECDH", SecObjectIdentifiers.SecP256r1); + + EncryptDecryptTest("X25519", MiscObjectIdentifiers.Curve25519); + + GnuPGCrossCheck(); Generate(); + + Generate25519(); + + EncryptDecryptX25519KeysTest(); } private void DoBasicKeyRingCheck(PgpPublicKeyRing pubKeyRing) diff --git a/crypto/test/src/openpgp/test/PgpEdDsaTest.cs b/crypto/test/src/openpgp/test/PgpEdDsaTest.cs new file mode 100644 index 0000000000..3a9d6c46b2 --- /dev/null +++ b/crypto/test/src/openpgp/test/PgpEdDsaTest.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using NUnit.Framework; +using Org.BouncyCastle.Asn1.Gnu; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests +{ + [TestFixture] + public class PgpEdDsaTest + : SimpleTest + { + private static readonly byte[] testPubKey = + Base64.Decode( + "mDMEX9NCKBYJKwYBBAHaRw8BAQdASPhAQySGGPMjoquv5i1IwLRSDJ2QtmLLvER2" + + "Cm8UZyW0HkVkRFNBIDx0ZXN0LmVkZHNhQGV4YW1wbGUuY29tPoiQBBMWCAA4FiEE" + + "sh83FOYApIfZuLp0emj2ffveCqEFAl/TQigCGwMFCwkIBwIGFQoJCAsCBBYCAwEC" + + "HgECF4AACgkQemj2ffveCqF6XQEA2S08fb0Z6LCd9P+eajPNDm1Wrf/y/7nkNwhb" + + "DvwiU5kBAM16UvHrzX6CvQFvc7aKvPH+4wrvRewvAGK16a4fBHEE"); + + private static readonly byte[] testPrivKey = + Base64.Decode( + "lIYEX9NCKBYJKwYBBAHaRw8BAQdASPhAQySGGPMjoquv5i1IwLRSDJ2QtmLLvER2" + + "Cm8UZyX+BwMC7ubvoFJTTXfOpQ3tDoys52w6tb01rHHtjKVWjXMjiyN8tXHBDC9N" + + "UcMYViTDegBXOEgw4TIKn9mkkTDvP3xVFeMH2XBPzu9e9m8GlBODILQeRWREU0Eg" + + "PHRlc3QuZWRkc2FAZXhhbXBsZS5jb20+iJAEExYIADgWIQSyHzcU5gCkh9m4unR6" + + "aPZ9+94KoQUCX9NCKAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRB6aPZ9" + + "+94KoXpdAQDZLTx9vRnosJ30/55qM80ObVat//L/ueQ3CFsO/CJTmQEAzXpS8evN" + + "foK9AW9ztoq88f7jCu9F7C8AYrXprh8EcQQ="); + + private static readonly char[] testPasswd = "test".ToCharArray(); + + private static readonly byte[] sExprKey = + Base64.Decode( + "KHByb3RlY3RlZC1wcml2YXRlLWtleSAoZWNjIChjdXJ2ZSBFZDI1NTE5KShmbGFn" + + "cyBlZGRzYSkocQogICM0MDQ4Rjg0MDQzMjQ4NjE4RjMyM0EyQUJBRkU2MkQ0OEMw" + + "QjQ1MjBDOUQ5MEI2NjJDQkJDNDQ3NjBBNkYxNDY3MjUjKQogKHByb3RlY3RlZCBv" + + "cGVucGdwLXMyazMtb2NiLWFlcyAoKHNoYTEgI0IwRkY2MDAzRUE4RkQ4QkIjCiAg" + + "Ijc2OTUzNjAiKSM5NDZEREU3QTUxMzAyRUEyRDc3NDNEOTQjKSM4NDBFMTIyRTdB" + + "RDI0RkY1MkE5RUY3QUFDQjgxRUE2CiAyMTkyQjZCMjlCOUI4N0QwNTZBOUE4MTEz" + + "QjIzNjlEREM4QUVGMTJDNjRBN0QwOTEwM0Q1MTU1Nzc0Q0Q5RkQ4NzczQTEzCiBD" + + "NTgwQ0Y4RkY5OEZERTU3RDVGIykocHJvdGVjdGVkLWF0ICIyMDIwMTIxMVQwOTU2" + + "MDEiKSkp"); + + private static readonly byte[] referencePubKey = + Base64.Decode( + "mDMEU/NfCxYJKwYBBAHaRw8BAQdAPwmJlL3ZFu1AUxl5NOSofIBzOhKA1i+AEJku" + + "Q+47JAY="); + + private static readonly string referenceMessage = "OpenPGP"; + + private static readonly byte[] referenceSignature = + Base64.Decode( + "iF4EABYIAAYFAlX5X5UACgkQjP3hIZeWWpr2IgEAVvkMypjiECY3vZg/2xbBMd/S" + + "ftgr9N3lYG4NdWrtM2YBANCcT6EVJ/A44PV/IgHYLy6iyQMyZfps60iehUuuYbQE"); + + private void ReferenceTest() + { + PgpPublicKeyRing pubKeyRing = new PgpPublicKeyRing(referencePubKey); + PgpPublicKey publicKey = pubKeyRing.GetPublicKey(); + + PgpObjectFactory pgpFact = new PgpObjectFactory(referenceSignature); + PgpSignatureList signatureList = (PgpSignatureList)pgpFact.NextPgpObject(); + PgpSignature signature = signatureList.Get(0); + signature.InitVerify(publicKey); + signature.Update(Encoding.ASCII.GetBytes(referenceMessage)); + if (!signature.Verify()) + { + Fail("signature failed to verify!"); + } + } + + private void GenerateAndSign() + { + SecureRandom random = SecureRandom.GetInstance("SHA1PRNG"); + + IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + keyGen.Init(new ECKeyGenerationParameters(GnuObjectIdentifiers.Ed25519, random)); + + AsymmetricCipherKeyPair kpSign = keyGen.GenerateKeyPair(); + + PgpKeyPair eddsaKeyPair = new PgpKeyPair(PublicKeyAlgorithmTag.EdDsa, kpSign, DateTime.UtcNow); + + byte[] msg = Encoding.ASCII.GetBytes("hello world!"); + + // + // try a signature + // + PgpSignatureGenerator signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.EdDsa, HashAlgorithmTag.Sha256); + signGen.InitSign(PgpSignature.BinaryDocument, eddsaKeyPair.PrivateKey); + + signGen.Update(msg); + + PgpSignature sig = signGen.Generate(); + + sig.InitVerify(eddsaKeyPair.PublicKey); + sig.Update(msg); + + if (!sig.Verify()) + { + Fail("signature failed to verify!"); + } + + // + // generate a key ring + // + char[] passPhrase = "test".ToCharArray(); + PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator(PgpSignature.PositiveCertification, eddsaKeyPair, + "test@bouncycastle.org", SymmetricKeyAlgorithmTag.Aes256, passPhrase, true, null, null, random); + + PgpPublicKeyRing pubRing = keyRingGen.GeneratePublicKeyRing(); + PgpSecretKeyRing secRing = keyRingGen.GenerateSecretKeyRing(); + + PgpPublicKeyRing pubRingEnc = new PgpPublicKeyRing(pubRing.GetEncoded()); + if (!Arrays.AreEqual(pubRing.GetEncoded(), pubRingEnc.GetEncoded())) + { + Fail("public key ring encoding failed"); + } + + PgpSecretKeyRing secRingEnc = new PgpSecretKeyRing(secRing.GetEncoded()); + if (!Arrays.AreEqual(secRing.GetEncoded(), secRingEnc.GetEncoded())) + { + Fail("secret key ring encoding failed"); + } + + + // + // try a signature using encoded key + // + signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.EdDsa, HashAlgorithmTag.Sha256); + signGen.InitSign(PgpSignature.BinaryDocument, secRing.GetSecretKey().ExtractPrivateKey(passPhrase)); + signGen.Update(msg); + + sig = signGen.Generate(); + sig.InitVerify(secRing.GetSecretKey().PublicKey); + sig.Update(msg); + + if (!sig.Verify()) + { + Fail("re-encoded signature failed to verify!"); + } + } + + public override void PerformTest() + { + ReferenceTest(); + + // + // Read the public key + // + PgpPublicKeyRing pubKeyRing = new PgpPublicKeyRing(testPubKey); + foreach (PgpSignature certification in pubKeyRing.GetPublicKey().GetSignatures()) + { + certification.InitVerify(pubKeyRing.GetPublicKey()); + + if (!certification.VerifyCertification((string)First(pubKeyRing.GetPublicKey().GetUserIds()), pubKeyRing.GetPublicKey())) + { + Fail("self certification does not verify"); + } + } + + /*if (pubKeyRing.GetPublicKey().BitStrength != 256) + { + Fail("incorrect bit strength returned"); + }*/ + + // + // Read the private key + // + PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing(testPrivKey); + + PgpPrivateKey privKey = secretKeyRing.GetSecretKey().ExtractPrivateKey(testPasswd); + + GenerateAndSign(); + + // + // sExpr + // + byte[] msg = Encoding.ASCII.GetBytes("hello world!"); + + PgpSecretKey key = PgpSecretKey.ParseSecretKeyFromSExpr(new MemoryStream(sExprKey, false), "test".ToCharArray()); + + PgpSignatureGenerator signGen = new PgpSignatureGenerator(PublicKeyAlgorithmTag.EdDsa, HashAlgorithmTag.Sha256); + signGen.InitSign(PgpSignature.BinaryDocument, key.ExtractPrivateKey(null)); + signGen.Update(msg); + + PgpSignature sig = signGen.Generate(); + sig.InitVerify(key.PublicKey); + sig.Update(msg); + + if (!sig.Verify()) + { + Fail("signature failed to verify!"); + } + } + + private static object First(IEnumerable e) + { + IEnumerator n = e.GetEnumerator(); + Assert.IsTrue(n.MoveNext()); + return n.Current; + } + + public override string Name + { + get { return "PgpEdDsaTest"; } + } + + public static void Main( + string[] args) + { + RunTest(new PgpECDsaTest()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + } +}