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  }