bcgit / bc-csharp

BouncyCastle.NET Cryptography Library (Mirror)
https://www.bouncycastle.org/csharp
MIT License
1.63k stars 547 forks source link

unknown PGP public key algorithm encountered (EDDSA?) #73

Open jstedfast opened 7 years ago

jstedfast commented 7 years ago

I'm working on adding logic to MimeKit to automatically fetch keys from a keyserver and import them when verifying signatures if an unknown key id is encountered (assuming MimeKit is configured to auto-fetch them).

I keep getting an exception about an unknown PGP public key algorithm when trying to read the returned stream. I modified my local copy of BouncyCastle to include the algorithm id in the IOException that gets thrown and the algorithm tag is 22 which doesn't have a mapping in the PublicKeyAlgorithmTag enum.

I took a quick look at rfc4880 and there does not appear to be an algorithm id of 22.

The following test case illustrates the problem.

Test Case:

using System;
using System.IO;
using System.Net.Http;

using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;

namespace KeyserverLookup {
    class Program
    {
        public static void Main (string[] args)
        {
            using (var stream = new MemoryStream ()) {
                using (var client = new HttpClient ()) {
                    var uri = new UriBuilder ();
                    uri.Scheme = "http";
                    uri.Host = "keys.gnupg.net";
                    uri.Port = 11371;
                    uri.Path = "/pks/lookup";
                    uri.Query = "op=get&search=0x24ECFF5AFF68370A";

                    using (var response = client.GetAsync (uri.ToString ()).GetAwaiter ().GetResult ()) {
                        response.Content.CopyToAsync (stream).GetAwaiter ().GetResult ();
                    }
                }

                stream.Position = 0;

                using (var armored = new ArmoredInputStream (stream)) {
                    var bundle = new PgpPublicKeyRingBundle (armored);

                    foreach (PgpPublicKeyRing keyring in bundle.GetKeyRings ()) {
                        foreach (PgpPublicKey key in keyring.GetPublicKeys ()) {
                            var valid = key.GetValidSeconds ();
                            var ctime = key.CreationTime;
                            var exp = ctime.AddSeconds (valid);
                            var now = DateTime.Now;
                            char algorithm = 'D';

                            if (!key.IsMasterKey && valid > 0 && now > exp)
                                continue;

                            switch (key.Algorithm) {
                            case PublicKeyAlgorithmTag.ECDH: algorithm = 'E'; break;
                            case PublicKeyAlgorithmTag.ElGamalEncrypt:
                            case PublicKeyAlgorithmTag.ElGamalGeneral: algorithm = 'g'; break;
                            case PublicKeyAlgorithmTag.RsaEncrypt:
                            case PublicKeyAlgorithmTag.RsaGeneral: algorithm = 'R'; break;
                            }

                            Console.Write ("{0}   {1}{2}/{3:X8} {4}",
                                key.IsMasterKey ? "pub" : "sub",
                                key.BitStrength,
                                algorithm,
                                (int) key.KeyId,
                                ctime.ToString ("yyyy-MM-dd"));
                            if (key.IsRevoked ()) {
                                Console.WriteLine (" [revoked: {0}]", exp.ToString ("yyyy-MM-dd"));
                            } else if (valid > 0) {
                                Console.WriteLine (" [expires: {0}]", exp.ToString ("yyyy-MM-dd"));
                            } else {
                                Console.WriteLine ();
                            }

                            foreach (string uid in key.GetUserIds ()) {
                                Console.WriteLine ("uid       {0}", uid);
                            }
                        }

                        Console.WriteLine ();
                    }
                }
            }
        }
    }
}

Error:

./keyserver-lookup.exe 

Unhandled Exception:
System.IO.IOException: unknown PGP public key algorithm encountered: 22
  at Org.BouncyCastle.Bcpg.PublicKeyPacket..ctor (Org.BouncyCastle.Bcpg.BcpgInputStream bcpgIn) [0x000fa] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at Org.BouncyCastle.Bcpg.PublicSubkeyPacket..ctor (Org.BouncyCastle.Bcpg.BcpgInputStream bcpgIn) [0x00000] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at Org.BouncyCastle.Bcpg.BcpgInputStream.ReadPacket () [0x002de] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at (wrapper remoting-invoke-with-check) Org.BouncyCastle.Bcpg.BcpgInputStream:ReadPacket ()
  at Org.BouncyCastle.Bcpg.OpenPgp.PgpPublicKeyRing.ReadSubkey (Org.BouncyCastle.Bcpg.BcpgInputStream bcpgInput) [0x00001] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at Org.BouncyCastle.Bcpg.OpenPgp.PgpPublicKeyRing..ctor (System.IO.Stream inputStream) [0x0009c] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at Org.BouncyCastle.Bcpg.OpenPgp.PgpObjectFactory.NextPgpObject () [0x00123] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at Org.BouncyCastle.Bcpg.OpenPgp.PgpObjectFactory.AllPgpObjects () [0x00013] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at Org.BouncyCastle.Bcpg.OpenPgp.PgpPublicKeyRingBundle..ctor (System.IO.Stream inputStream) [0x00007] in <db94dee80fb04a8f9f9551caee933b45>:0 
  at LoadKeyBlockSample.Program.Main (System.String[] args) [0x000b2] in <69e5296966704328a35074713cc14eaa>:0 
jstedfast commented 7 years ago

If I use gpg --export 24ECFF5AFF68370A | gpg --list-packets, I get:

:public sub key packet:
    version 4, algo 22, created 1415374702, expires 0
    unknown algorithm 22

So it definitely looks like a broken packet.

It would be nice if there were a graceful way to handle this, though...

jstedfast commented 7 years ago

Hmmm, I'm getting the same problem for 0x4F0540D577F95F95 which also hits algorithm 22.

jstedfast commented 7 years ago

Looks like algorithm 22 is EDDSA. https://github.com/open-keychain/open-keychain/issues/1279

jstedfast commented 7 years ago

Latest draft that I can find: https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04

jstedfast commented 5 years ago

FWIW, this is an example file downloaded from a pgp keyserver which illustrates this problem:

pubkey.txt

jstedfast commented 5 years ago

Latest draft spec for this: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07

stoyandimov commented 4 years ago

I'm am interacting with Passbolt's API and they cloud server key is apparently also EDDSA. This makes it veeery difficult for me to work with BouncyCastle.

Are there plans to include that algorithm?

jstedfast commented 4 years ago

Latest draft spec: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09

@stoyandimov I have not heard anything.