github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/kex2_secret.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 "errors" 8 "github.com/keybase/client/go/kex2" 9 keybase1 "github.com/keybase/client/go/protocol/keybase1" 10 "golang.org/x/crypto/scrypt" 11 "strings" 12 ) 13 14 const kexPhraseVersion = "four" 15 16 type Kex2Secret struct { 17 phrase string 18 secret kex2.Secret 19 typ Kex2SecretType 20 } 21 22 type Kex2SecretType int 23 24 const ( 25 Kex2SecretTypeNone Kex2SecretType = 0 26 Kex2SecretTypeV1Desktop Kex2SecretType = 1 27 Kex2SecretTypeV1Mobile Kex2SecretType = 2 28 Kex2SecretTypeV2 Kex2SecretType = 3 29 ) 30 31 func NewKex2SecretFromTypeAndUID(typ Kex2SecretType, uid keybase1.UID) (*Kex2Secret, error) { 32 33 entropy := Kex2PhraseEntropy 34 if typ == Kex2SecretTypeV2 { 35 entropy = Kex2PhraseEntropy2 36 } 37 38 words, err := SecWordList(entropy) 39 if err != nil { 40 return nil, err 41 } 42 43 phrase := strings.Join(words, " ") 44 // If we are provisioning a mobile device, we want to use an easier to compute secret. In order to 45 // communicate that to the two devices involved in kex without breaking the existing protocol, 46 // we have added an extra word that is not in the dictionary. Up to date clients can see this 47 // word and use the lighter version of scrypt. 48 if typ == Kex2SecretTypeV1Mobile { 49 phrase += " " + kexPhraseVersion 50 } 51 return newKex2SecretFromTypeUIDAndPhrase(typ, uid, phrase) 52 } 53 54 func NewKex2SecretFromUIDAndPhrase(uid keybase1.UID, phrase string) (*Kex2Secret, error) { 55 56 typ, err := kex2TypeFromPhrase(phrase) 57 if err != nil { 58 return nil, err 59 } 60 61 return newKex2SecretFromTypeUIDAndPhrase(typ, uid, phrase) 62 } 63 64 func kex2TypeFromPhrase(phrase string) (typ Kex2SecretType, err error) { 65 66 words := strings.Split(phrase, " ") 67 if len(words) == 8 { 68 return Kex2SecretTypeV1Desktop, nil 69 } 70 if len(words) != 9 { 71 return Kex2SecretTypeNone, errors.New("wrong number of words in passphrase; wanted 8 or 9") 72 } 73 if words[len(words)-1] == kexPhraseVersion { 74 return Kex2SecretTypeV1Mobile, nil 75 } 76 return Kex2SecretTypeV2, nil 77 } 78 79 func newKex2SecretFromTypeUIDAndPhrase(typ Kex2SecretType, uid keybase1.UID, phrase string) (*Kex2Secret, error) { 80 81 var cost int 82 var salt []byte 83 switch typ { 84 case Kex2SecretTypeV1Mobile: 85 cost = Kex2ScryptLiteCost 86 case Kex2SecretTypeV1Desktop: 87 cost = Kex2ScryptCost 88 case Kex2SecretTypeV2: 89 cost = Kex2ScryptLiteCost 90 salt = uid.ToBytes() 91 default: 92 return nil, errors.New("unknown kex2 secret type") 93 } 94 95 key, err := scrypt.Key([]byte(phrase), salt, cost, Kex2ScryptR, Kex2ScryptP, Kex2ScryptKeylen) 96 if err != nil { 97 return nil, err 98 } 99 res := &Kex2Secret{phrase: phrase, typ: typ} 100 copy(res.secret[:], key) 101 return res, nil 102 } 103 104 func (s *Kex2Secret) Secret() kex2.Secret { 105 return s.secret 106 } 107 108 func (s *Kex2Secret) Phrase() string { 109 return s.phrase 110 }