github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_kid_reuse_test.go (about) 1 package engine 2 3 import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/elliptic" 7 "crypto/rand" 8 "testing" 9 "time" 10 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/stretchr/testify/require" 14 15 "github.com/keybase/go-crypto/curve25519" 16 "github.com/keybase/go-crypto/openpgp" 17 "github.com/keybase/go-crypto/openpgp/ecdh" 18 "github.com/keybase/go-crypto/openpgp/packet" 19 ) 20 21 func genKeyWithMaterial(uid *packet.UserId, currentTime time.Time, signingPriv *ecdsa.PrivateKey, 22 encryptPriv *ecdh.PrivateKey) *openpgp.Entity { 23 24 // We will be manipulating key creation time while keeping the eddsa 25 // private key bytes same. This will yield different PGP fingerprint 26 // every time (and different full hash), but same Keybase KID. 27 28 signingPrivKey := packet.NewECDSAPrivateKey(currentTime, signingPriv) 29 entity := &openpgp.Entity{ 30 PrimaryKey: &signingPrivKey.PublicKey, 31 PrivateKey: signingPrivKey, 32 Identities: make(map[string]*openpgp.Identity), 33 } 34 isPrimaryID := true 35 entity.Identities[uid.Id] = &openpgp.Identity{ 36 Name: uid.Name, 37 UserId: uid, 38 SelfSignature: &packet.Signature{ 39 CreationTime: currentTime, 40 SigType: packet.SigTypePositiveCert, 41 PubKeyAlgo: packet.PubKeyAlgoECDSA, 42 Hash: crypto.SHA512, 43 IsPrimaryId: &isPrimaryID, 44 FlagsValid: true, 45 FlagSign: true, 46 FlagCertify: true, 47 IssuerKeyId: &entity.PrimaryKey.KeyId, 48 }, 49 } 50 encryptPrivKey := packet.NewECDHPrivateKey(currentTime, encryptPriv) 51 subkey := openpgp.Subkey{ 52 PublicKey: &encryptPrivKey.PublicKey, 53 PrivateKey: encryptPrivKey, 54 Sig: &packet.Signature{ 55 CreationTime: currentTime, 56 SigType: packet.SigTypeSubkeyBinding, 57 PubKeyAlgo: packet.PubKeyAlgoECDSA, 58 Hash: crypto.SHA512, 59 FlagsValid: true, 60 FlagEncryptStorage: true, 61 FlagEncryptCommunications: true, 62 IssuerKeyId: &entity.PrimaryKey.KeyId, 63 }, 64 } 65 subkey.PrivateKey.IsSubkey = true 66 subkey.PublicKey.IsSubkey = true 67 entity.Subkeys = append(entity.Subkeys, subkey) 68 return entity 69 } 70 71 func TestY2K1178(t *testing.T) { 72 // Test PGP KID reuse by using the same key material for primary key. 73 74 // PGP keys are not uniquely identified by KID. KID is only generated by 75 // hashing the primary key public key material. The rest is malleable. 76 // Different uids, subkeys, and signatures will yields the same key but 77 // also the same PGP fingerprint. However, it is possible to generate new 78 // PGP key with the same key material but different creation time. This 79 // will yield a new PGP fingerprint but same Keybase KID. 80 81 // There was an issue where we weren't properly setting `ActivePGPHash` 82 // when new PGP key was being delegated after key with the same KID was 83 // revoked. 84 85 // During sigchain parsing, we would go through: 86 // 1) Delegate key A with kid=X - new key, ActivePGPHash is set to A. 87 // 2) Revoke kid=X. keys[X] is set to Revoked, and ActivePGPHash to "". 88 // 3) Delegate key B with kid=X - there already is X, set status to 89 // Uncancelled (forgot about ActivePGPHash here). 90 91 // This resulted in sigchain not being replayable because we were taking 92 // PGP key A to verify reverse sig of PGP key B in link 3. 93 94 signingPriv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) 95 require.NoError(t, err) 96 // Encryption key material doesn't matter, we can keep it the same or make 97 // a new one for each generated key. KID is only based on primary key 98 // material. 99 encPriv, err := ecdh.GenerateKey(curve25519.Cv25519(), rand.Reader) 100 require.NoError(t, err) 101 102 uid := packet.NewUserId("Keybase PGP Test", "Test Only Do Not Use", "alice@example.com") 103 104 encode := func(entity *openpgp.Entity) []byte { 105 buf, err := encodeArmoredPrivatePGP(entity) 106 require.NoError(t, err) 107 return buf.Bytes() 108 } 109 110 currentTime := time.Now() 111 112 entity1 := genKeyWithMaterial(uid, currentTime, signingPriv, encPriv) 113 privKey1 := encode(entity1) 114 115 currentTime2 := currentTime.Add(-24 * time.Hour) 116 entity2 := genKeyWithMaterial(uid, currentTime2, signingPriv, encPriv) 117 privKey2 := encode(entity2) 118 119 tc := SetupEngineTest(t, "pgp") 120 defer tc.Cleanup() 121 122 user := CreateAndSignupFakeUser(tc, "pgp") 123 secui := &libkb.TestSecretUI{Passphrase: user.Passphrase} 124 uis := libkb.UIs{LogUI: tc.G.UI.GetLogUI(), SecretUI: secui} 125 126 mctx := NewMetaContextForTest(tc).WithUIs(uis) 127 128 var kid keybase1.KID 129 130 { 131 // Add first PGP key. 132 eng, err := NewPGPKeyImportEngineFromBytes(tc.G, privKey1, false) 133 require.NoError(t, err) 134 err = RunEngine2(mctx, eng) 135 require.NoError(t, err) 136 kid = eng.bundle.GetKID() 137 } 138 139 { 140 // Revoke that key. 141 eng := NewRevokeKeyEngine(tc.G, kid) 142 err := RunEngine2(mctx, eng) 143 require.NoError(t, err) 144 } 145 146 var delegate2Err error 147 { 148 // Add second key. It should have the same KID as the first one. 149 eng, err := NewPGPKeyImportEngineFromBytes(tc.G, privKey2, false) 150 require.NoError(t, err) 151 // Do not care about an error from this engine immediately, keep going. 152 delegate2Err = RunEngine2(mctx, eng) 153 kid2 := eng.bundle.GetKID() 154 require.Equal(t, kid, kid2) 155 } 156 157 { 158 // Try to identify that user 159 tc := SetupEngineTest(t, "pgp") 160 defer tc.Cleanup() 161 162 idUI := &FakeIdentifyUI{} 163 arg := keybase1.Identify2Arg{ 164 UserAssertion: user.Username, 165 UseDelegateUI: false, 166 CanSuppressUI: true, 167 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 168 } 169 170 uis := libkb.UIs{ 171 LogUI: tc.G.UI.GetLogUI(), 172 IdentifyUI: idUI, 173 } 174 eng := NewResolveThenIdentify2(tc.G, &arg) 175 mctx := NewMetaContextForTest(tc).WithUIs(uis) 176 err = RunEngine2(mctx, eng) 177 require.NoError(t, err) 178 } 179 180 // Check PGP import engine error. When this bug was first reported, that 181 // engine was erroring out but the key was still being added. 182 require.NoError(t, delegate2Err) 183 }