github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/paperkey_phrase.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/sha256" 8 "errors" 9 "strings" 10 ) 11 12 // PaperKeyPhrase is the string that can generate a paper key. It 13 // is versioned and forced to be lowercase. To make a new random 14 // phrase, use MakePaperKeyPhrase. To convert a string to a 15 // PaperKeyPhrase, use NewPaperKeyPhrase. 16 type PaperKeyPhrase string 17 18 // MakePaperKeyPhrase creates a new, random paper key phrase for 19 // the given version. 20 func MakePaperKeyPhrase(version uint8) (PaperKeyPhrase, error) { 21 nbits := PaperKeySecretEntropy + PaperKeyIDBits + PaperKeyVersionBits 22 for i := 0; i < 1000; i++ { 23 words, err := SecWordList(nbits) 24 if err != nil { 25 return "", err 26 } 27 if wordVersion(words[len(words)-1]) != version { 28 continue 29 } 30 return NewPaperKeyPhrase(strings.Join(words, " ")), nil 31 } 32 return "", KeyGenError{Msg: "exhausted attempts to generate valid paper key"} 33 } 34 35 // NewPaperKeyPhrase converts a string into a PaperKeyPhrase. 36 func NewPaperKeyPhrase(phrase string) PaperKeyPhrase { 37 phrase = strings.TrimSpace(strings.ToLower(phrase)) 38 return PaperKeyPhrase(strings.Join(strings.Fields(phrase), " ")) 39 } 40 41 // String returns a string representation of the phrase. 42 func (p PaperKeyPhrase) String() string { 43 return string(p) 44 } 45 46 // Bytes returns a byte slice of the phrase. 47 func (p PaperKeyPhrase) Bytes() []byte { 48 return []byte(p) 49 } 50 51 // Version calculates the phrase version. 0-15 are possible 52 // versions. 53 func (p PaperKeyPhrase) Version() (uint8, error) { 54 words := p.words() 55 if len(words) == 0 { 56 return 0, errors.New("empty paper key phrase") 57 } 58 return wordVersion(words[len(words)-1]), nil 59 } 60 61 func (p PaperKeyPhrase) InvalidWords() (words []string) { 62 for _, w := range p.words() { 63 // in secwords.go: 64 if !ValidSecWord(w) { 65 words = append(words, w) 66 } 67 } 68 return words 69 } 70 71 // Prefix returns the first two words in the phrase. 72 func (p PaperKeyPhrase) Prefix() string { 73 return strings.Join(p.words()[0:2], " ") 74 } 75 76 // words returns the phrase as a slice of words. 77 func (p PaperKeyPhrase) words() []string { 78 return strings.Fields(p.String()) 79 } 80 81 func (p PaperKeyPhrase) NumWords() int { 82 return len(p.words()) 83 } 84 85 // wordVersion calculates the paper key phrase version based on a 86 // word. 87 func wordVersion(word string) uint8 { 88 h := sha256.Sum256([]byte(word)) 89 if PaperKeyVersionBits > 8 { 90 panic("PaperKeyVersionBits must be 8 bits or fewer") 91 } 92 return h[len(h)-1] & ((1 << PaperKeyVersionBits) - 1) 93 } 94 95 func NewPaperKeyPhraseCheckVersion(m MetaContext, passphrase string) (ret PaperKeyPhrase, err error) { 96 paperPhrase := NewPaperKeyPhrase(passphrase) 97 version, err := paperPhrase.Version() 98 if err != nil { 99 return ret, err 100 } 101 if version != PaperKeyVersion { 102 m.Debug("paper version mismatch: generated paper key version = %d, libkb version = %d", version, PaperKeyVersion) 103 return ret, KeyVersionError{} 104 } 105 return paperPhrase, nil 106 }