github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/puk_roll.go (about) 1 // Copyright 2017 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 // PerUserKeyRoll creates a new per-user-key for the active user. 5 // This can be the first per-user-key for the user. 6 package engine 7 8 import ( 9 "fmt" 10 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/protocol/keybase1" 13 ) 14 15 // PerUserKeyRoll is an engine. 16 type PerUserKeyRoll struct { 17 libkb.Contextified 18 args *PerUserKeyRollArgs 19 DidNewKey bool 20 } 21 22 type PerUserKeyRollArgs struct { 23 Me *libkb.User // optional 24 } 25 26 // NewPerUserKeyRoll creates a PerUserKeyRoll engine. 27 func NewPerUserKeyRoll(g *libkb.GlobalContext, args *PerUserKeyRollArgs) *PerUserKeyRoll { 28 return &PerUserKeyRoll{ 29 args: args, 30 Contextified: libkb.NewContextified(g), 31 } 32 } 33 34 // Name is the unique engine name. 35 func (e *PerUserKeyRoll) Name() string { 36 return "PerUserKeyRoll" 37 } 38 39 // GetPrereqs returns the engine prereqs. 40 func (e *PerUserKeyRoll) Prereqs() Prereqs { 41 return Prereqs{ 42 Device: true, 43 } 44 } 45 46 // RequiredUIs returns the required UIs. 47 func (e *PerUserKeyRoll) RequiredUIs() []libkb.UIKind { 48 return []libkb.UIKind{} 49 } 50 51 // SubConsumers returns the other UI consumers for this engine. 52 func (e *PerUserKeyRoll) SubConsumers() []libkb.UIConsumer { 53 return []libkb.UIConsumer{} 54 } 55 56 // Run starts the engine. 57 func (e *PerUserKeyRoll) Run(mctx libkb.MetaContext) (err error) { 58 defer mctx.Trace("PerUserKeyRoll", &err)() 59 return retryOnEphemeralRace(mctx, e.inner) 60 } 61 62 func (e *PerUserKeyRoll) inner(mctx libkb.MetaContext) error { 63 var err error 64 65 uid := mctx.G().GetMyUID() 66 if uid.IsNil() { 67 return libkb.NoUIDError{} 68 } 69 70 me := e.args.Me 71 if me == nil { 72 mctx.Debug("PerUserKeyRoll load self") 73 74 loadArg := libkb.NewLoadUserArgWithMetaContext(mctx). 75 WithUID(uid). 76 WithSelf(true). 77 WithPublicKeyOptional() 78 me, err = libkb.LoadUser(loadArg) 79 if err != nil { 80 return err 81 } 82 } 83 meUPAK := me.ExportToUserPlusAllKeys() 84 85 sigKey, err := mctx.ActiveDevice().SigningKey() 86 if err != nil { 87 return fmt.Errorf("signing key not found: (%v)", err) 88 } 89 encKey, err := mctx.ActiveDevice().EncryptionKey() 90 if err != nil { 91 return fmt.Errorf("encryption key not found: (%v)", err) 92 } 93 94 pukring, err := mctx.G().GetPerUserKeyring(mctx.Ctx()) 95 if err != nil { 96 return err 97 } 98 err = pukring.Sync(mctx) 99 if err != nil { 100 return err 101 } 102 103 // Generation of the new key 104 gen := pukring.CurrentGeneration() + keybase1.PerUserKeyGeneration(1) 105 mctx.Debug("PerUserKeyRoll creating gen: %v", gen) 106 107 pukSeed, err := libkb.GeneratePerUserKeySeed() 108 if err != nil { 109 return err 110 } 111 112 var pukPrev *libkb.PerUserKeyPrev 113 if gen > 1 { 114 pukPrevInner, err := pukring.PreparePrev(mctx, pukSeed, gen) 115 if err != nil { 116 return err 117 } 118 pukPrev = &pukPrevInner 119 } 120 121 pukReceivers, err := e.getPukReceivers(mctx, &meUPAK) 122 if err != nil { 123 return err 124 } 125 if len(pukReceivers) == 0 { 126 return fmt.Errorf("no receivers") 127 } 128 129 // Create boxes of the new per-user-key 130 pukBoxes, err := pukring.PrepareBoxesForDevices(mctx, 131 pukSeed, gen, pukReceivers, encKey) 132 if err != nil { 133 return err 134 } 135 136 mctx.Debug("PerUserKeyRoll make sigs") 137 sig, err := libkb.PerUserKeyProofReverseSigned(mctx, me, pukSeed, gen, sigKey) 138 if err != nil { 139 return err 140 } 141 // Seqno when the per-user-key will be signed in. 142 pukSeqno := sig.Seqno 143 144 payload := make(libkb.JSONPayload) 145 payload["sigs"] = []libkb.JSONPayload{sig.Payload} 146 147 mctx.Debug("PerUserKeyRoll pukBoxes:%v pukPrev:%v for generation %v", 148 len(pukBoxes), pukPrev != nil, gen) 149 libkb.AddPerUserKeyServerArg(payload, gen, pukBoxes, pukPrev) 150 151 ekLib := mctx.G().GetEKLib() 152 var myUserEKBox *keybase1.UserEkBoxed 153 var newUserEKMetadata *keybase1.UserEkMetadata 154 if ekLib != nil { 155 merkleRoot, err := mctx.G().GetMerkleClient().FetchRootFromServer(mctx, libkb.EphemeralKeyMerkleFreshness) 156 if err != nil { 157 return err 158 } 159 sig, boxes, newMetadata, myBox, err := ekLib.PrepareNewUserEK(mctx, *merkleRoot, pukSeed) 160 if err != nil { 161 return err 162 } 163 // If there are no active deviceEKs, we can't publish this key. This 164 // should mostly only come up in tests. 165 if len(boxes) > 0 { 166 myUserEKBox = myBox 167 newUserEKMetadata = &newMetadata 168 userEKSection := make(libkb.JSONPayload) 169 userEKSection["sig"] = sig 170 userEKSection["boxes"] = boxes 171 payload["user_ek"] = userEKSection 172 } else { 173 mctx.Debug("skipping userEK publishing, there are no valid deviceEKs") 174 } 175 } 176 177 mctx.Debug("PerUserKeyRoll post") 178 _, err = mctx.G().API.PostJSON(mctx, libkb.APIArg{ 179 Endpoint: "key/multi", 180 SessionType: libkb.APISessionTypeREQUIRED, 181 JSONPayload: payload, 182 }) 183 if err != nil { 184 return err 185 } 186 if err = libkb.MerkleCheckPostedUserSig(mctx, uid, pukSeqno, sig.LinkID); err != nil { 187 return err 188 } 189 e.DidNewKey = true 190 191 // Add the per-user-key locally 192 err = pukring.AddKey(mctx, gen, pukSeqno, pukSeed) 193 if err != nil { 194 return err 195 } 196 197 // Add the new userEK box to local storage, if it was created above. 198 if myUserEKBox != nil { 199 err = mctx.G().GetUserEKBoxStorage().Put(mctx, newUserEKMetadata.Generation, *myUserEKBox) 200 if err != nil { 201 mctx.Error("error while saving userEK box: %s", err) 202 } 203 } 204 205 mctx.G().UserChanged(mctx.Ctx(), uid) 206 return nil 207 } 208 209 // Get the receivers of the new per-user-key boxes. 210 // Includes all the user's device subkeys. 211 func (e *PerUserKeyRoll) getPukReceivers(mctx libkb.MetaContext, meUPAK *keybase1.UserPlusAllKeys) (res []libkb.NaclDHKeyPair, err error) { 212 for _, dk := range meUPAK.Base.DeviceKeys { 213 if !dk.IsSibkey && !dk.IsRevoked { 214 receiver, err := libkb.ImportNaclDHKeyPairFromHex(dk.KID.String()) 215 if err != nil { 216 return res, err 217 } 218 res = append(res, receiver) 219 } 220 } 221 return res, nil 222 }