github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/pgp_gen.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "crypto" 8 "fmt" 9 "strings" 10 11 "github.com/keybase/go-crypto/openpgp" 12 "github.com/keybase/go-crypto/openpgp/errors" 13 "github.com/keybase/go-crypto/openpgp/packet" 14 "github.com/keybase/go-crypto/openpgp/s2k" 15 "github.com/keybase/go-crypto/rsa" 16 ) 17 18 type PGPGenArg struct { 19 PrimaryBits int 20 SubkeyBits int 21 Ids Identities 22 Config *packet.Config 23 PGPUids []string 24 PrimaryLifetime int 25 SubkeyLifetime int 26 } 27 28 func ui32p(i int) *uint32 { 29 if i >= 0 { 30 tmp := uint32(i) 31 return &tmp 32 } 33 return nil 34 } 35 36 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a 37 // single identity composed of the given full name, comment and email, any of 38 // which may be empty but must not contain any of "()<>\x00". 39 // If config is nil, sensible defaults will be used. 40 // 41 // Modification of: https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=8fec09c61d5d66f460d227fd1df3473d7e015bc6#456 42 // 43 // From golang.com/x/crypto/openpgp/keys.go 44 func GeneratePGPKeyBundle(g *GlobalContext, arg PGPGenArg, logUI LogUI) (*PGPKeyBundle, error) { 45 currentTime := arg.Config.Now() 46 47 if len(arg.Ids) == 0 { 48 return nil, errors.InvalidArgumentError("No Ids in PGPArg") 49 } 50 uids, err := arg.PGPUserIDs() 51 if err != nil { 52 return nil, err 53 } 54 for i, id := range arg.Ids { 55 extra := "" 56 if i == 0 { 57 extra = "[primary]" 58 } 59 if logUI != nil { 60 logUI.Info("PGP User ID: %s %s", id, extra) 61 } 62 } 63 64 if logUI != nil { 65 logUI.Info("Generating primary key (%d bits)", arg.PrimaryBits) 66 } 67 masterPriv, err := rsa.GenerateKey(arg.Config.Random(), arg.PrimaryBits) 68 if err != nil { 69 return nil, err 70 } 71 72 if logUI != nil { 73 logUI.Info("Generating encryption subkey (%d bits)", arg.SubkeyBits) 74 } 75 encryptingPriv, err := rsa.GenerateKey(arg.Config.Random(), arg.SubkeyBits) 76 if err != nil { 77 return nil, err 78 } 79 80 e := &openpgp.Entity{ 81 PrimaryKey: packet.NewRSAPublicKey(currentTime, &masterPriv.PublicKey), 82 PrivateKey: packet.NewRSAPrivateKey(currentTime, masterPriv), 83 Identities: make(map[string]*openpgp.Identity), 84 } 85 86 for i, uid := range uids { 87 isPrimaryID := true 88 if i > 0 { 89 isPrimaryID = false 90 } 91 id := &openpgp.Identity{ 92 Name: uid.Name, 93 UserId: uid, 94 SelfSignature: &packet.Signature{ 95 CreationTime: currentTime, 96 SigType: packet.SigTypePositiveCert, 97 PubKeyAlgo: packet.PubKeyAlgoRSA, 98 Hash: arg.Config.Hash(), 99 IsPrimaryId: &isPrimaryID, 100 FlagsValid: true, 101 FlagSign: true, 102 FlagCertify: true, 103 IssuerKeyId: &e.PrimaryKey.KeyId, 104 PreferredSymmetric: arg.PreferredSymmetric(), 105 PreferredHash: arg.PreferredHash(), 106 PreferredCompression: arg.PreferredCompression(), 107 }, 108 } 109 id.SelfSignature.KeyLifetimeSecs = ui32p(arg.PrimaryLifetime) 110 e.Identities[uid.Id] = id 111 } 112 113 e.Subkeys = make([]openpgp.Subkey, 1) 114 e.Subkeys[0] = openpgp.Subkey{ 115 PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey), 116 PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv), 117 Sig: &packet.Signature{ 118 CreationTime: currentTime, 119 SigType: packet.SigTypeSubkeyBinding, 120 PubKeyAlgo: packet.PubKeyAlgoRSA, 121 Hash: arg.Config.Hash(), 122 FlagsValid: true, 123 FlagEncryptStorage: true, 124 FlagEncryptCommunications: true, 125 IssuerKeyId: &e.PrimaryKey.KeyId, 126 PreferredSymmetric: arg.PreferredSymmetric(), 127 PreferredHash: arg.PreferredHash(), 128 PreferredCompression: arg.PreferredCompression(), 129 }, 130 } 131 e.Subkeys[0].PublicKey.IsSubkey = true 132 e.Subkeys[0].PrivateKey.IsSubkey = true 133 e.Subkeys[0].Sig.KeyLifetimeSecs = ui32p(arg.SubkeyLifetime) 134 135 return NewGeneratedPGPKeyBundle(e), nil 136 } 137 138 // CreateIDs creates identities for KeyGenArg.Ids if none exist. 139 // It uses PGPUids to determine the set of Ids. It does not set the 140 // default keybase.io uid. AddDefaultUid() does that. 141 func (a *PGPGenArg) CreatePGPIDs() error { 142 if len(a.Ids) > 0 { 143 return nil 144 } 145 for _, id := range a.PGPUids { 146 if !strings.Contains(id, "<") && CheckEmail.F(id) { 147 a.Ids = append(a.Ids, Identity{Email: id}) 148 continue 149 } 150 parsed, err := ParseIdentity(id) 151 if err != nil { 152 return err 153 } 154 a.Ids = append(a.Ids, *parsed) 155 } 156 return nil 157 } 158 159 // Just for testing 160 func (a *PGPGenArg) AddDefaultUID(g *GlobalContext) { 161 a.Ids = append(a.Ids, KeybaseIdentity(g, "")) 162 } 163 164 // Just for testing 165 func (a *PGPGenArg) MakeAllIds(g *GlobalContext) error { 166 if err := a.CreatePGPIDs(); err != nil { 167 return err 168 } 169 a.AddDefaultUID(g) 170 return nil 171 } 172 173 func (a *PGPGenArg) PGPUserIDs() ([]*packet.UserId, error) { 174 uids := make([]*packet.UserId, len(a.Ids)) 175 for i, id := range a.Ids { 176 uids[i] = id.ToPGPUserID() 177 if uids[i] == nil { 178 return nil, fmt.Errorf("Id[%d] failed to convert to PGPUserId (%+v)", i, id) 179 } 180 } 181 return uids, nil 182 } 183 184 func (a *PGPGenArg) Init() (err error) { 185 defBits := 4096 186 if a.PrimaryBits == 0 { 187 a.PrimaryBits = defBits 188 } 189 if a.SubkeyBits == 0 { 190 a.SubkeyBits = defBits 191 } 192 if a.PrimaryLifetime == 0 { 193 a.PrimaryLifetime = KeyExpireIn 194 } 195 if a.SubkeyLifetime == 0 { 196 a.SubkeyLifetime = SubkeyExpireIn 197 } 198 return 199 } 200 201 func (a *PGPGenArg) PreferredSymmetric() []uint8 { 202 return []uint8{ 203 uint8(packet.CipherAES128), 204 uint8(packet.CipherAES256), 205 uint8(packet.CipherCAST5), 206 } 207 } 208 209 func (a *PGPGenArg) PreferredHash() []uint8 { 210 gohash := []crypto.Hash{ 211 crypto.SHA256, 212 crypto.SHA512, 213 crypto.SHA1, 214 crypto.RIPEMD160, 215 } 216 var res []uint8 217 for _, h := range gohash { 218 id, ok := s2k.HashToHashId(h) 219 if !ok { 220 continue 221 } 222 res = append(res, id) 223 } 224 return res 225 } 226 227 func (a *PGPGenArg) PreferredCompression() []uint8 { 228 return []uint8{ 229 uint8(packet.CompressionNone), 230 uint8(packet.CompressionZIP), 231 uint8(packet.CompressionZLIB), 232 } 233 }