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