pgpainless / pgpainless

Simple to use OpenPGP API based on Bouncy Castle
https://pgpainless.org
Apache License 2.0
155 stars 23 forks source link

Wrong primary user ID when setting withAdditionalUserId #60

Closed bilalashraf123 closed 3 years ago

bilalashraf123 commented 3 years ago

Generated a key withAdditionalUserId using the following code

PGPSecretKeyRing keyRing = PGPainless.generateKeyRing()
                .withSubKey(
                        KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
                        .withKeyFlags(KeyFlag.SIGN_DATA)
                        .withDetailedConfiguration()
                        .withDefaultSymmetricAlgorithms()
                        .withDefaultHashAlgorithms()
                        .withPreferredCompressionAlgorithms(CompressionAlgorithm.ZLIB)
                        .withFeature(Feature.MODIFICATION_DETECTION)
                        .done())
                .withSubKey(
                        KeySpec.getBuilder(ECDH.fromCurve(EllipticCurve._P256))
                        .withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
                        .withDefaultAlgorithms())
                .withMasterKey(
                        KeySpec.getBuilder(RSA.withLength(RsaLength._2048))
                                .withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)
                                .withDefaultAlgorithms())
                .withPrimaryUserId("primary@primary.com")
                .withAdditionalUserId("secondary@secondary.com")
                .withoutPassphrase()
                .build();

        ByteArrayOutputStream keyOut = new ByteArrayOutputStream();
        ArmoredOutputStream armoredOut = new ArmoredOutputStream(keyOut);
        keyRing.encode(armoredOut);
        armoredOut.close();
        keyOut.close();

When parsed using the following command, it does not show the primary user ID

C:\Data\Softwares\FlowCrypt\temp>gpg --list-packets pgpainless_private.key
# off=0 ctb=95 tag=5 hlen=3 plen=920
:secret key packet:
        version 4, algo 1, created 1611704887, expires 0
        pkey[0]: [2048 bits]
        pkey[1]: [17 bits]
        skey[2]: [2046 bits]
        skey[3]: [1024 bits]
        skey[4]: [1024 bits]
        skey[5]: [1024 bits]
        checksum: 4703
        keyid: A11ECBF1F2B31478
# off=923 ctb=b4 tag=13 hlen=2 plen=19
:user ID packet: "primary@primary.com"
# off=944 ctb=89 tag=2 hlen=3 plen=307
:signature packet: algo 1, keyid A11ECBF1F2B31478
        version 4, created 1611704887, md5len 0, sigclass 0x13
        digest algo 10, begin of digest 07 36
        hashed subpkt 2 len 4 (sig created 2021-01-26)
        hashed subpkt 27 len 1 (key flags: 03)
        hashed subpkt 22 len 4 (pref-zip-algos: 2 3 1 0)
        hashed subpkt 11 len 3 (pref-sym-algos: 9 8 7)
        hashed subpkt 21 len 4 (pref-hash-algos: 10 9 8 11)
        hashed subpkt 30 len 1 (features: 01)
        subpkt 16 len 8 (issuer key ID A11ECBF1F2B31478)
        data: [2045 bits]
# off=1254 ctb=b4 tag=13 hlen=2 plen=23
:user ID packet: "secondary@secondary.com"
# off=1279 ctb=89 tag=2 hlen=3 plen=284
:signature packet: algo 1, keyid A11ECBF1F2B31478
        version 4, created 1611704887, md5len 0, sigclass 0x13
        digest algo 10, begin of digest f5 2b
        hashed subpkt 2 len 4 (sig created 2021-01-26)
        subpkt 16 len 8 (issuer key ID A11ECBF1F2B31478)
        data: [2044 bits]
...........

I created the key using direct BouncyCastle code and it worked fine. Here is the output from the BouncyCastle code

C:\Data\Softwares\FlowCrypt\temp>gpg --list-packets bc_private.key
# off=0 ctb=95 tag=5 hlen=3 plen=966
:secret key packet:
        version 4, algo 3, created 1611704896, expires 0
        pkey[0]: [2048 bits]
        pkey[1]: [17 bits]
        iter+salt S2K, algo: 9, SHA1 protection, hash: 2, salt: A4E83938863CF817
        protect count: 65536 (96)
        protect IV:  8f 1f 4d b1 14 2c 12 84 41 6b 61 db e5 12 8c 58
        skey[2]: [v4 protected]
        keyid: 8F1972B83CE60206
# off=969 ctb=b4 tag=13 hlen=2 plen=19
:user ID packet: "primary@primary.com"
# off=990 ctb=89 tag=2 hlen=3 plen=316
:signature packet: algo 3, keyid 8F1972B83CE60206
        version 4, created 1611704898, md5len 0, sigclass 0x13
        digest algo 2, begin of digest 74 d9
        hashed subpkt 2 len 4 (sig created 2021-01-26)
        critical hashed subpkt 9 len 4 (key expires after 2y2d0h0m)
        critical hashed subpkt 25 len 1 (primary user ID)
        critical hashed subpkt 11 len 3 (pref-sym-algos: 9 8 2)
        critical hashed subpkt 21 len 5 (pref-hash-algos: 2 10 9 8 3)
        critical hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
        critical hashed subpkt 30 len 1 (features: 01)
        critical hashed subpkt 27 len 1 (key flags: 03)
        subpkt 16 len 8 (issuer key ID 8F1972B83CE60206)
        data: [2047 bits]
# off=1309 ctb=b4 tag=13 hlen=2 plen=23
:user ID packet: "secondary@secondary.com"
# off=1334 ctb=89 tag=2 hlen=3 plen=284
:signature packet: algo 3, keyid 8F1972B83CE60206
        version 4, created 1611704899, md5len 0, sigclass 0x13
        digest algo 2, begin of digest 39 dd
        hashed subpkt 2 len 4 (sig created 2021-01-26)
        subpkt 16 len 8 (issuer key ID 8F1972B83CE60206)
        data: [2048 bits]
........
tomholub commented 3 years ago

@bilalashraf123 wanted to try and fix this in pgpainless as a way of getting familiar with the library - he'll have a look and produce a PR

tomholub commented 3 years ago

How could this issue be tested? Since it was fixed without adding any test, it may get broken in the future.

I agree, I didn't find a way in PGPainless to check for primary user ID value. It gives all the users list for which test was already added.

Normally I assume the first returned uid is the primary one. Is that guaranteed in pgpainless? Or is actually the way to choose primary uid a different mechanism?

vanitasvitae commented 3 years ago

Closed via #61