summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2024-04-10 17:02:37 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2024-04-10 17:02:37 +0700
commit67f26538ce499d3d201fe217709542ba8360ebff (patch)
tree27845cdbc49b88405170c32a766711cbd83178b6
parentFix CCM input length check (diff)
downloadBouncyCastle.NET-ed25519-67f26538ce499d3d201fe217709542ba8360ebff.tar.xz
Add various fingerprint-related methods in OpenPgp
-rw-r--r--crypto/src/openpgp/PgpPublicKey.cs16
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRing.cs19
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRingBundle.cs72
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRing.cs48
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRingBundle.cs34
-rw-r--r--crypto/test/src/openpgp/test/PGPRSATest.cs14
-rw-r--r--crypto/test/src/openpgp/test/PgpEdDsaTest.cs6
7 files changed, 153 insertions, 56 deletions
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index fa924ff37..672ce9548 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Drawing;
 using System.IO;
 
 using Org.BouncyCastle.Asn1.Cryptlib;
@@ -11,6 +10,7 @@ using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Math.EC.Rfc7748;
@@ -115,14 +115,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
             else
             {
-                this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56)
-                    | ((ulong)fingerprint[fingerprint.Length - 7] << 48)
-                    | ((ulong)fingerprint[fingerprint.Length - 6] << 40)
-                    | ((ulong)fingerprint[fingerprint.Length - 5] << 32)
-                    | ((ulong)fingerprint[fingerprint.Length - 4] << 24)
-                    | ((ulong)fingerprint[fingerprint.Length - 3] << 16)
-                    | ((ulong)fingerprint[fingerprint.Length - 2] << 8)
-                    | (ulong)fingerprint[fingerprint.Length - 1]);
+                this.keyId = (long)Pack.BE_To_UInt64(fingerprint, fingerprint.Length - 8);
 
                 if (key is RsaPublicBcpgKey)
                 {
@@ -490,6 +483,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return (byte[]) fingerprint.Clone();
         }
 
+        public bool HasFingerprint(byte[] fingerprint)
+        {
+            return Arrays.AreEqual(this.fingerprint, fingerprint);
+        }
+
         /// <summary>
         /// Check if this key has an algorithm type that makes it suitable to use for encryption.
         /// </summary>
diff --git a/crypto/src/openpgp/PgpPublicKeyRing.cs b/crypto/src/openpgp/PgpPublicKeyRing.cs
index 46eecd726..f50dd915c 100644
--- a/crypto/src/openpgp/PgpPublicKeyRing.cs
+++ b/crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 
-using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
@@ -80,6 +79,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return null;
         }
 
+        /// <summary>Return the public key with the passed in fingerprint if it is present.</summary>
+        public virtual PgpPublicKey GetPublicKey(byte[] fingerprint)
+        {
+            foreach (PgpPublicKey k in keys)
+            {
+                if (k.HasFingerprint(fingerprint))
+                    return k;
+            }
+
+            return null;
+        }
+
         /// <summary>Allows enumeration of all the public keys.</summary>
         /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns>
         public virtual IEnumerable<PgpPublicKey> GetPublicKeys()
@@ -239,17 +250,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public static PgpPublicKeyRing Join(PgpPublicKeyRing first, PgpPublicKeyRing second, bool joinTrustPackets,
             bool allowSubkeySigsOnNonSubkey)
         {
-            if (!Arrays.AreEqual(first.GetPublicKey().GetFingerprint(), second.GetPublicKey().GetFingerprint()))
+            if (!second.GetPublicKey().HasFingerprint(first.GetPublicKey().GetFingerprint()))
                 throw new ArgumentException("Cannot merge certificates with differing primary keys.");
 
             var secondKeys = new HashSet<long>();
-            foreach (var key in second.GetPublicKeys())
+            foreach (var key in second.keys)
             {
                 secondKeys.Add(key.KeyId);
             }
 
             var merged = new List<PgpPublicKey>();
-            foreach (var key in first.GetPublicKeys())
+            foreach (var key in first.keys)
             {
                 var copy = second.GetPublicKey(key.KeyId);
                 if (copy != null)
diff --git a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
index 473d0ae5b..1940c979e 100644
--- a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
+++ b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
@@ -8,10 +8,10 @@ using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>
-	/// Often a PGP key ring file is made up of a succession of master/sub-key key rings.
-	/// If you want to read an entire public key file in one hit this is the class for you.
-	/// </remarks>
+    /// <remarks>
+    /// Often a PGP key ring file is made up of a succession of master/sub-key key rings.
+    /// If you want to read an entire public key file in one hit this is the class for you.
+    /// </remarks>
     public class PgpPublicKeyRingBundle
     {
         private readonly IDictionary<long, PgpPublicKeyRing> m_pubRings;
@@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <summary>Allow enumeration of the public key rings making up this collection.</summary>
         public IEnumerable<PgpPublicKeyRing> GetKeyRings()
         {
-			return CollectionUtilities.Proxy(m_pubRings.Values);
+			return CollectionUtilities.Proxy(KeyRings);
         }
 
 		/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
@@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			var compareInfo = CultureInfo.InvariantCulture.CompareInfo;
 			var compareOptions = ignoreCase ? CompareOptions.OrdinalIgnoreCase : CompareOptions.Ordinal;
 
-			foreach (PgpPublicKeyRing pubRing in GetKeyRings())
+			foreach (PgpPublicKeyRing pubRing in KeyRings)
 			{
 				foreach (string nextUserID in pubRing.GetPublicKey().GetUserIds())
 				{
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <param name="keyId">The ID of the public key to return.</param>
         public PgpPublicKey GetPublicKey(long keyId)
         {
-            foreach (PgpPublicKeyRing pubRing in GetKeyRings())
+            foreach (PgpPublicKeyRing pubRing in KeyRings)
             {
                 PgpPublicKey pub = pubRing.GetPublicKey(keyId);
 				if (pub != null)
@@ -135,7 +135,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			if (m_pubRings.TryGetValue(keyId, out var keyRing))
 				return keyRing;
 
-			foreach (PgpPublicKeyRing pubRing in GetKeyRings())
+			foreach (PgpPublicKeyRing pubRing in KeyRings)
             {
                 if (pubRing.GetPublicKey(keyId) != null)
                     return pubRing;
@@ -144,11 +144,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return null;
         }
 
-		/// <summary>
-		/// Return true if a key matching the passed in key ID is present, false otherwise.
-		/// </summary>
-		/// <param name="keyID">key ID to look for.</param>
-		public bool Contains(long keyID)
+        /// <summary>Return the PGP public key associated with the given key fingerprint.</summary>
+        /// <param name="fingerprint">the public key fingerprint to match against.</param>
+        public PgpPublicKey GetPublicKey(byte[] fingerprint)
+        {
+            foreach (PgpPublicKeyRing pubRing in KeyRings)
+            {
+                PgpPublicKey pub = pubRing.GetPublicKey(fingerprint);
+                if (pub != null)
+                    return pub;
+            }
+
+            return null;
+        }
+
+        /// <summary>Return the public key ring which contains the key associated with the given key fingerprint.
+        /// </summary>
+        /// <param name="fingerprint">the public key fingerprint to match against.</param>
+        public PgpPublicKeyRing GetPublicKeyRing(byte[] fingerprint)
+        {
+            foreach (PgpPublicKeyRing pubRing in KeyRings)
+            {
+                if (pubRing.GetPublicKey(fingerprint) != null)
+                    return pubRing;
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Return true if a key matching the passed in key ID is present, false otherwise.
+        /// </summary>
+        /// <param name="keyID">key ID to look for.</param>
+        public bool Contains(long keyID)
 		{
 			return GetPublicKey(keyID) != null;
 		}
@@ -170,14 +198,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		/// <summary>
-		/// Return a new bundle containing the contents of the passed in bundle and
-		/// the passed in public key ring.
-		/// </summary>
-		/// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be added to.</param>
-		/// <param name="publicKeyRing">The key ring to be added.</param>
-		/// <returns>A new <c>PgpPublicKeyRingBundle</c> merging the current one with the passed in key ring.</returns>
-		/// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception>
+        private ICollection<PgpPublicKeyRing> KeyRings => m_pubRings.Values;
+
+        /// <summary>
+        /// Return a new bundle containing the contents of the passed in bundle and
+        /// the passed in public key ring.
+        /// </summary>
+        /// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be added to.</param>
+        /// <param name="publicKeyRing">The key ring to be added.</param>
+        /// <returns>A new <c>PgpPublicKeyRingBundle</c> merging the current one with the passed in key ring.</returns>
+        /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception>
         public static PgpPublicKeyRingBundle AddPublicKeyRing(PgpPublicKeyRingBundle bundle,
             PgpPublicKeyRing publicKeyRing)
         {
diff --git a/crypto/src/openpgp/PgpSecretKeyRing.cs b/crypto/src/openpgp/PgpSecretKeyRing.cs
index a070aa132..ff644545f 100644
--- a/crypto/src/openpgp/PgpSecretKeyRing.cs
+++ b/crypto/src/openpgp/PgpSecretKeyRing.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.IO;
 
 using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
@@ -115,6 +114,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return keys[0].PublicKey;
         }
 
+        /// <summary>Return the public key referred to by the passed in keyID if it is present.</summary>
+        public PgpPublicKey GetPublicKey(long keyID)
+        {
+            PgpSecretKey key = GetSecretKey(keyID);
+            if (key != null)
+                return key.PublicKey;
+
+            foreach (PgpPublicKey k in extraPubKeys)
+            {
+                if (keyID == k.KeyId)
+                    return k;
+            }
+
+            return null;
+        }
+
+        /// <summary>Return the public key with the passed in fingerprint if it is present.</summary>
+        public PgpPublicKey GetPublicKey(byte[] fingerprint)
+        {
+            PgpSecretKey key = GetSecretKey(fingerprint);
+            if (key != null)
+                return key.PublicKey;
+
+            foreach (PgpPublicKey k in extraPubKeys)
+            {
+                if (k.HasFingerprint(fingerprint))
+                    return k;
+            }
+
+            return null;
+        }
+
         /**
          * Return any keys carrying a signature issued by the key represented by keyID.
          *
@@ -165,6 +196,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return CollectionUtilities.Proxy(keys);
         }
 
+        /// <summary>Return the secret key referred to by the passed in keyID if it is present.</summary>
         public PgpSecretKey GetSecretKey(long keyId)
         {
             foreach (PgpSecretKey k in keys)
@@ -176,6 +208,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return null;
         }
 
+        /// <summary>Return the secret key associated with the passed in fingerprint if it is present.</summary>
+        public PgpSecretKey GetSecretKey(byte[] fingerprint)
+        {
+            foreach (PgpSecretKey k in keys)
+            {
+                if (k.PublicKey.HasFingerprint(fingerprint))
+                    return k;
+            }
+
+            return null;
+        }
+
         /// <summary>
         /// Return an iterator of the public keys in the secret key ring that
         /// have no matching private key. At the moment only personal certificate data
@@ -247,7 +291,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
             var newKeys = new List<PgpSecretKey>(ring.keys.Count);
 
-            foreach (PgpSecretKey secretKey in ring.GetSecretKeys())
+            foreach (PgpSecretKey secretKey in ring.keys)
             {
                 if (secretKey.IsPrivateKeyEmpty)
                 {
diff --git a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
index 695c882b7..fe9a2461a 100644
--- a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
+++ b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
@@ -8,10 +8,10 @@ using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>
-	/// Often a PGP key ring file is made up of a succession of master/sub-key key rings.
-	/// If you want to read an entire secret key file in one hit this is the class for you.
-	/// </remarks>
+    /// <remarks>
+    /// Often a PGP key ring file is made up of a succession of master/sub-key key rings.
+    /// If you want to read an entire secret key file in one hit this is the class for you.
+    /// </remarks>
     public class PgpSecretKeyRingBundle
     {
         private readonly IDictionary<long, PgpSecretKeyRing> m_secretRings;
@@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <summary>Allow enumeration of the secret key rings making up this collection.</summary>
 		public IEnumerable<PgpSecretKeyRing> GetKeyRings()
         {
-            return CollectionUtilities.Proxy(m_secretRings.Values);
+            return CollectionUtilities.Proxy(KeyRings);
         }
 
 		/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
@@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			var compareInfo = CultureInfo.InvariantCulture.CompareInfo;
 			var compareOptions = ignoreCase ? CompareOptions.OrdinalIgnoreCase : CompareOptions.Ordinal;
 
-			foreach (PgpSecretKeyRing secRing in GetKeyRings())
+			foreach (PgpSecretKeyRing secRing in KeyRings)
 			{
 				foreach (string nextUserID in secRing.GetSecretKey().UserIds)
 				{
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <param name="keyId">The ID of the secret key to return.</param>
 		public PgpSecretKey GetSecretKey(long keyId)
         {
-            foreach (PgpSecretKeyRing secRing in GetKeyRings())
+            foreach (PgpSecretKeyRing secRing in KeyRings)
             {
                 PgpSecretKey sec = secRing.GetSecretKey(keyId);
 				if (sec != null)
@@ -135,7 +135,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			if (m_secretRings.TryGetValue(keyId, out var keyRing))
 				return keyRing;
 
-			foreach (PgpSecretKeyRing secretRing in GetKeyRings())
+			foreach (PgpSecretKeyRing secretRing in KeyRings)
             {
                 if (secretRing.GetSecretKey(keyId) != null)
                     return secretRing;
@@ -170,14 +170,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		/// <summary>
-		/// Return a new bundle containing the contents of the passed in bundle and
-		/// the passed in secret key ring.
-		/// </summary>
-		/// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be added to.</param>
-		/// <param name="secretKeyRing">The key ring to be added.</param>
-		/// <returns>A new <c>PgpSecretKeyRingBundle</c> merging the current one with the passed in key ring.</returns>
-		/// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception>
+        private ICollection<PgpSecretKeyRing> KeyRings => m_secretRings.Values;
+
+        /// <summary>
+        /// Return a new bundle containing the contents of the passed in bundle and
+        /// the passed in secret key ring.
+        /// </summary>
+        /// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be added to.</param>
+        /// <param name="secretKeyRing">The key ring to be added.</param>
+        /// <returns>A new <c>PgpSecretKeyRingBundle</c> merging the current one with the passed in key ring.</returns>
+        /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception>
         public static PgpSecretKeyRingBundle AddSecretKeyRing(PgpSecretKeyRingBundle bundle,
             PgpSecretKeyRing secretKeyRing)
         {
diff --git a/crypto/test/src/openpgp/test/PGPRSATest.cs b/crypto/test/src/openpgp/test/PGPRSATest.cs
index 6de95fbeb..56d761c16 100644
--- a/crypto/test/src/openpgp/test/PGPRSATest.cs
+++ b/crypto/test/src/openpgp/test/PGPRSATest.cs
@@ -325,7 +325,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
 
             PgpPublicKey pubKey = pgpPub.GetPublicKey();
 
-            if (!Arrays.AreEqual(pubKey.GetFingerprint(), Hex.Decode("4FFB9F0884266C715D1CEAC804A3BBFA")))
+            byte[] expectedVersion3 = Hex.Decode("4FFB9F0884266C715D1CEAC804A3BBFA");
+            if (!Arrays.AreEqual(pubKey.GetFingerprint(), expectedVersion3))
+            {
+                Fail("version 3 fingerprint test failed");
+            }
+            if (!pubKey.HasFingerprint(expectedVersion3))
             {
                 Fail("version 3 fingerprint test failed");
             }
@@ -337,10 +342,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
 
             pubKey = pgpPub.GetPublicKey();
 
-            if (!Arrays.AreEqual(pubKey.GetFingerprint(), Hex.Decode("3062363c1046a01a751946bb35586146fdf3f373")))
+            byte[] expectedVersion4 = Hex.Decode("3062363c1046a01a751946bb35586146fdf3f373");
+            if (!Arrays.AreEqual(pubKey.GetFingerprint(), expectedVersion4))
             {
                Fail("version 4 fingerprint test failed");
             }
+            if (!pubKey.HasFingerprint(expectedVersion4))
+            {
+                Fail("version 4 fingerprint test failed");
+            }
         }
 
         private void MixedTest(
diff --git a/crypto/test/src/openpgp/test/PgpEdDsaTest.cs b/crypto/test/src/openpgp/test/PgpEdDsaTest.cs
index f67d19a7f..c3cdd53f0 100644
--- a/crypto/test/src/openpgp/test/PgpEdDsaTest.cs
+++ b/crypto/test/src/openpgp/test/PgpEdDsaTest.cs
@@ -202,8 +202,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
 
             PgpPublicKeyRing pubKeyRing = new PgpPublicKeyRing(aIn);
 
-            IsTrue(AreEqual(Hex.Decode("EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E"),
-                pubKeyRing.GetPublicKey().GetFingerprint()));
+            IsTrue(AreEqual(pubKeyRing.GetPublicKey().GetFingerprint(),
+                Hex.Decode("EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E")));
+            IsTrue(pubKeyRing.GetPublicKey().HasFingerprint(
+                Hex.Decode("EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E")));
 
             aIn = new ArmoredInputStream(new MemoryStream(Strings.ToByteArray(edDSASecretKey), false));