summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpPublicKey.cs
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-11-05 15:40:09 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-11-05 15:40:09 +0700
commit7f43ba84bd30b99d0e957920aa660a45f998c522 (patch)
tree7c6f4738d69ab3def1416c397752a3b63b66738f /crypto/src/openpgp/PgpPublicKey.cs
parentUpdate package icon (diff)
downloadBouncyCastle.NET-ed25519-7f43ba84bd30b99d0e957920aa660a45f998c522.tar.xz
Port OpenPGP support for XDH, EdDSA from bc-java
- see https://github.com/bcgit/bc-csharp/issues/345
Diffstat (limited to 'crypto/src/openpgp/PgpPublicKey.cs')
-rw-r--r--crypto/src/openpgp/PgpPublicKey.cs311
1 files changed, 222 insertions, 89 deletions
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index cdb8efd36..400cda071 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -3,13 +3,18 @@ using System.Collections.Generic;
 using System.IO;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cryptlib;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Gnu;
+using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Generators;
 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;
@@ -18,7 +23,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
     /// <remarks>General class to handle a PGP public key object.</remarks>
     public class PgpPublicKey
+        : PgpObject
     {
+        // We default to these as they are specified as mandatory in RFC 6631.
+        private static readonly PgpKdfParameters DefaultKdfParameters = new PgpKdfParameters(HashAlgorithmTag.Sha256,
+            SymmetricKeyAlgorithmTag.Aes128);
+
         public static byte[] CalculateFingerprint(PublicKeyPacket publicPk)
         {
             IBcpgKey key = publicPk.Key;
@@ -30,7 +40,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                 try
                 {
-                    digest = DigestUtilities.GetDigest("MD5");
+                    digest = PgpUtilities.CreateDigest(HashAlgorithmTag.MD5);
+
                     UpdateDigest(digest, rK.Modulus);
                     UpdateDigest(digest, rK.PublicExponent);
                 }
@@ -45,7 +56,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                     byte[] kBytes = publicPk.GetEncodedContents();
 
-                    digest = DigestUtilities.GetDigest("SHA1");
+                    digest = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1);
 
                     digest.Update(0x99);
                     digest.Update((byte)(kBytes.Length >> 8));
@@ -124,27 +135,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                     this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength;
                 }
-                else if (key is ECPublicBcpgKey)
+                else if (key is EdDsaPublicBcpgKey eddsaK)
                 {
-                    DerObjectIdentifier curveOid = ((ECPublicBcpgKey)key).CurveOid;
-                    if (GnuObjectIdentifiers.Ed25519.Equals(curveOid)
-                        //|| CryptlibObjectIdentifiers.curvey25519.Equals(curveOid)
-                        )
+                    var curveOid = eddsaK.CurveOid;
+                    if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) ||
+                        GnuObjectIdentifiers.Ed25519.Equals(curveOid) ||
+                        EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                        CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
                     {
                         this.keyStrength = 256;
                     }
+                    else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid) ||
+                        EdECObjectIdentifiers.id_X448.Equals(curveOid))
+                    {
+                        this.keyStrength = 448;
+                    }
                     else
                     {
-                        X9ECParametersHolder ecParameters = ECKeyPairGenerator.FindECCurveByOidLazy(curveOid);
-
-                        if (ecParameters != null)
-                        {
-                            this.keyStrength = ecParameters.Curve.FieldSize;
-                        }
-                        else
-                        {
-                            this.keyStrength = -1; // unknown
-                        }
+                        this.keyStrength = -1; // unknown
+                    }
+                }
+                else if (key is ECPublicBcpgKey ecK)
+                {
+                    var curveOid = ecK.CurveOid;
+                    X9ECParametersHolder ecParameters = ECKeyPairGenerator.FindECCurveByOidLazy(curveOid);
+
+                    if (ecParameters != null)
+                    {
+                        this.keyStrength = ecParameters.Curve.FieldSize;
+                    }
+                    else
+                    {
+                        this.keyStrength = -1; // unknown
                     }
                 }
             }
@@ -165,7 +187,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public PgpPublicKey(PublicKeyAlgorithmTag algorithm, AsymmetricKeyParameter pubKey, DateTime time)
         {
             if (pubKey.IsPrivate)
-                throw new ArgumentException("Expected a public key", "pubKey");
+                throw new ArgumentException("Expected a public key", nameof(pubKey));
 
             IBcpgKey bcpgKey;
             if (pubKey is RsaKeyParameters rK)
@@ -178,6 +200,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                 bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y);
             }
+            else if (pubKey is ElGamalPublicKeyParameters eK)
+            {
+                ElGamalParameters eS = eK.Parameters;
+
+                bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y);
+            }
             else if (pubKey is ECPublicKeyParameters ecK)
             {
                 if (algorithm == PublicKeyAlgorithmTag.ECDH)
@@ -194,11 +222,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     throw new PgpException("unknown EC algorithm");
                 }
             }
-            else if (pubKey is ElGamalPublicKeyParameters eK)
+            else if (pubKey is Ed25519PublicKeyParameters ed25519PubKey)
             {
-                ElGamalParameters eS = eK.Parameters;
+                byte[] pointEnc = new byte[1 + Ed25519PublicKeyParameters.KeySize];
+                pointEnc[0] = 0x40;
+                ed25519PubKey.Encode(pointEnc, 1);
+                bcpgKey = new EdDsaPublicBcpgKey(GnuObjectIdentifiers.Ed25519, new BigInteger(1, pointEnc));
+            }
+            else if (pubKey is Ed448PublicKeyParameters ed448PubKey)
+            {
+                byte[] pointEnc = new byte[Ed448PublicKeyParameters.KeySize];
+                ed448PubKey.Encode(pointEnc, 0);
+                bcpgKey = new EdDsaPublicBcpgKey(EdECObjectIdentifiers.id_Ed448, new BigInteger(1, pointEnc));
+            }
+            else if (pubKey is X25519PublicKeyParameters x25519PubKey)
+            {
+                byte[] pointEnc = new byte[1 + X25519PublicKeyParameters.KeySize];
+                pointEnc[0] = 0x40;
+                x25519PubKey.Encode(pointEnc, 1);
 
-                bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y);
+                PgpKdfParameters kdfParams = DefaultKdfParameters;
+
+                bcpgKey = new ECDHPublicBcpgKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, pointEnc),
+                    kdfParams.HashAlgorithm, kdfParams.SymmetricWrapAlgorithm);
+            }
+            else if (pubKey is X448PublicKeyParameters x448PubKey)
+            {
+                byte[] pointEnc = new byte[X448PublicKeyParameters.KeySize];
+                x448PubKey.Encode(pointEnc, 0);
+
+                PgpKdfParameters kdfParams = DefaultKdfParameters;
+
+                bcpgKey = new ECDHPublicBcpgKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, pointEnc),
+                    kdfParams.HashAlgorithm, kdfParams.SymmetricWrapAlgorithm);
             }
             else
             {
@@ -473,24 +529,89 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             {
                 switch (publicPk.Algorithm)
                 {
-                    case PublicKeyAlgorithmTag.RsaEncrypt:
-                    case PublicKeyAlgorithmTag.RsaGeneral:
-                    case PublicKeyAlgorithmTag.RsaSign:
-                        RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key;
-                        return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
-                    case PublicKeyAlgorithmTag.Dsa:
-                        DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key;
-                        return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
-                    case PublicKeyAlgorithmTag.ECDsa:
-                        return GetECKey("ECDSA");
-                    case PublicKeyAlgorithmTag.ECDH:
-                        return GetECKey("ECDH");
-                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
-                    case PublicKeyAlgorithmTag.ElGamalGeneral:
-                        ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key;
-                        return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G));
-                    default:
-                        throw new PgpException("unknown public key algorithm encountered");
+                case PublicKeyAlgorithmTag.RsaEncrypt:
+                case PublicKeyAlgorithmTag.RsaGeneral:
+                case PublicKeyAlgorithmTag.RsaSign:
+                    RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key;
+                    return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
+                case PublicKeyAlgorithmTag.Dsa:
+                    DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key;
+                    return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
+                case PublicKeyAlgorithmTag.ECDsa:
+                    ECDsaPublicBcpgKey ecdsaK = (ECDsaPublicBcpgKey)publicPk.Key;
+                    return GetECKey("ECDSA", ecdsaK);
+                case PublicKeyAlgorithmTag.ECDH:
+                {
+                    ECDHPublicBcpgKey ecdhK = (ECDHPublicBcpgKey)publicPk.Key;
+                    var curveOid = ecdhK.CurveOid;
+
+                    if (EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                        CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + X25519.PointSize, ecdhK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid X25519 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else if (EdECObjectIdentifiers.id_X448.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + X448.PointSize, ecdhK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid X448 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else
+                    {
+                        return GetECKey("ECDH", ecdhK);
+                    }
+                }
+                case PublicKeyAlgorithmTag.EdDsa:
+                {
+                    EdDsaPublicBcpgKey eddsaK = (EdDsaPublicBcpgKey)publicPk.Key;
+                    var curveOid = eddsaK.CurveOid;
+
+                    if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) ||
+                        GnuObjectIdentifiers.Ed25519.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + Ed25519.PublicKeySize, eddsaK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid Ed25519 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + Ed448.PublicKeySize, eddsaK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid Ed448 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else 
+                    {
+                        throw new InvalidOperationException();
+                    }
+                }
+                case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                case PublicKeyAlgorithmTag.ElGamalGeneral:
+                    ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key;
+                    return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G));
+                default:
+                    throw new PgpException("unknown public key algorithm encountered");
                 }
             }
             catch (PgpException e)
@@ -503,9 +624,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-        private ECPublicKeyParameters GetECKey(string algorithm)
+        private ECPublicKeyParameters GetECKey(string algorithm, ECPublicBcpgKey ecK)
         {
-            ECPublicBcpgKey ecK = (ECPublicBcpgKey)publicPk.Key;
             X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(ecK.CurveOid);
             BigInteger encodedPoint = ecK.EncodedPoint;
 
@@ -563,7 +683,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public IEnumerable<PgpSignature> GetSignaturesForId(string id)
         {
             if (id == null)
-                throw new ArgumentNullException("id");
+                throw new ArgumentNullException(nameof(id));
 
             var result = new List<PgpSignature>();
             bool userIdFound = false;
@@ -580,13 +700,48 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return userIdFound ? CollectionUtilities.Proxy(result) : null;
         }
 
+        private IEnumerable<PgpSignature> GetSignaturesForID(UserIdPacket id)
+        {
+            var signatures = new List<PgpSignature>();
+            bool userIdFound = false;
+
+            for (int i = 0; i != ids.Count; i++)
+            {
+                if (id.Equals(ids[i]))
+                {
+                    userIdFound = true;
+                    signatures.AddRange(idSigs[i]);
+                }
+            }
+
+            return userIdFound ? signatures : null;
+        }
+
+        /// <summary>Return any signatures associated with the passed in key identifier keyID.</summary>
+        /// <param name="keyID">the key id to be matched.</param>
+        /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects issued by the key with keyID.</returns>
+        public IEnumerable<PgpSignature> GetSignaturesForKeyID(long keyID)
+        {
+            var sigs = new List<PgpSignature>();
+
+            foreach (var sig in GetSignatures())
+            {
+                if (sig.KeyId == keyID)
+                {
+                    sigs.Add(sig);
+                }
+            }
+
+            return CollectionUtilities.Proxy(sigs);
+        }
+
         /// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary>
         /// <param name="userAttributes">The vector of user attributes to be matched.</param>
         /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
         public IEnumerable<PgpSignature> GetSignaturesForUserAttribute(PgpUserAttributeSubpacketVector userAttributes)
         {
             if (userAttributes == null)
-                throw new ArgumentNullException("userAttributes");
+                throw new ArgumentNullException(nameof(userAttributes));
 
             var result = new List<PgpSignature>();
             bool attributeFound = false;
@@ -648,11 +803,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
          */
         public IEnumerable<PgpSignature> GetKeySignatures()
         {
-            var result = subSigs;
-            if (result == null)
-            {
-                result = new List<PgpSignature>(keySigs);
-            }
+            var result = subSigs ?? new List<PgpSignature>(keySigs);
 
             return CollectionUtilities.Proxy(result);
         }
@@ -947,56 +1098,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         /// <param name="key">The key the certifications are to be removed from.</param>
         /// <param name="certification">The certfication to be removed.</param>
         /// <returns>The modified key, null if the certification was not found.</returns>
-        public static PgpPublicKey RemoveCertification(
-            PgpPublicKey	key,
-            PgpSignature	certification)
+        public static PgpPublicKey RemoveCertification(PgpPublicKey	key, PgpSignature certification)
         {
-            PgpPublicKey returnKey = new PgpPublicKey(key);
-            var sigs = returnKey.subSigs != null
-                ?	returnKey.subSigs
-                :	returnKey.keySigs;
+            var returnKey = new PgpPublicKey(key);
+            var sigs = returnKey.subSigs ?? returnKey.keySigs;
 
-//			bool found = sigs.Remove(certification);
-            int pos = sigs.IndexOf(certification);
-            bool found = pos >= 0;
+            if (sigs.Remove(certification))
+                return returnKey;
 
-            if (found)
+            // TODO Java uses getRawUserIDs
+            foreach (string id in key.GetUserIds())
             {
-                sigs.RemoveAt(pos);
+                if (ContainsSignature(key.GetSignaturesForId(id), certification))
+                    return RemoveCertification(returnKey, id, certification);
             }
-            else
-            {
-                foreach (string id in key.GetUserIds())
-                {
-                    foreach (object sig in key.GetSignaturesForId(id))
-                    {
-                        // TODO Is this the right type of equality test?
-                        if (certification == sig)
-                        {
-                            found = true;
-                            returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification);
-                        }
-                    }
-                }
 
-                if (!found)
-                {
-                    foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes())
-                    {
-                        foreach (object sig in key.GetSignaturesForUserAttribute(id))
-                        {
-                            // TODO Is this the right type of equality test?
-                            if (certification == sig)
-                            {
-                                found = true;
-                                returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification);
-                            }
-                        }
-                    }
-                }
+            foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes())
+            {
+                if (ContainsSignature(key.GetSignaturesForUserAttribute(id), certification))
+                    return RemoveCertification(returnKey, id, certification);
             }
 
             return returnKey;
         }
+
+        private static bool ContainsSignature(IEnumerable<PgpSignature> signatures, PgpSignature signature)
+        {
+            foreach (PgpSignature candidate in signatures)
+            {
+                if (signature == candidate)
+                    return true;
+            }
+            return false;
+        }
     }
 }