Merge pull request #773 from Roasbeef/aezeed
aezeed: add new package implementing the aezeed cipher seed scheme
This commit is contained in:
commit
1ba399267b
69
aezeed/bench_test.go
Normal file
69
aezeed/bench_test.go
Normal file
@ -0,0 +1,69 @@
|
||||
package aezeed
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
mnemonic Mnemonic
|
||||
|
||||
seed *CipherSeed
|
||||
)
|
||||
|
||||
// BenchmarkFrommnemonic benchmarks the process of converting a cipher seed
|
||||
// (given the salt), to an enciphered mnemonic.
|
||||
func BenchmarkTomnemonic(b *testing.B) {
|
||||
scryptN = 32768
|
||||
scryptR = 8
|
||||
scryptP = 1
|
||||
|
||||
pass := []byte("1234567890abcedfgh")
|
||||
cipherSeed, err := New(0, nil, time.Now())
|
||||
if err != nil {
|
||||
b.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
var r Mnemonic
|
||||
for i := 0; i < b.N; i++ {
|
||||
r, err = cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
b.Fatalf("unable to encipher: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
|
||||
mnemonic = r
|
||||
}
|
||||
|
||||
// BenchmarkToCipherSeed benchmarks the process of deciphering an existing
|
||||
// enciphered mnemonic.
|
||||
func BenchmarkToCipherSeed(b *testing.B) {
|
||||
scryptN = 32768
|
||||
scryptR = 8
|
||||
scryptP = 1
|
||||
|
||||
pass := []byte("1234567890abcedfgh")
|
||||
cipherSeed, err := New(0, nil, time.Now())
|
||||
if err != nil {
|
||||
b.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
b.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
var s *CipherSeed
|
||||
for i := 0; i < b.N; i++ {
|
||||
s, err = mnemonic.ToCipherSeed(pass)
|
||||
if err != nil {
|
||||
b.Fatalf("unable to decipher: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
|
||||
seed = s
|
||||
}
|
547
aezeed/cipherseed.go
Normal file
547
aezeed/cipherseed.go
Normal file
@ -0,0 +1,547 @@
|
||||
package aezeed
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Yawning/aez"
|
||||
"github.com/kkdai/bstream"
|
||||
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
// CipherSeedVersion is the current version of the aezeed scheme as
|
||||
// defined in this package. This version indicates the following
|
||||
// parameters for the deciphered cipher seed: a 1 byte version, 2 bytes
|
||||
// for the Bitcoin Days Genesis timestamp, and 16 bytes for entropy. It
|
||||
// also governs how the cipher seed should be enciphered. In this
|
||||
// version we take the deciphered seed, create a 5 byte salt, use that
|
||||
// with an optional passphrase to generate a 32-byte key (via scrypt),
|
||||
// then encipher with aez (using the salt and version as AD). The final
|
||||
// enciphered seed is: version || ciphertext || salt.
|
||||
CipherSeedVersion uint8 = 0
|
||||
|
||||
// DecipheredCipherSeedSize is the size of the plaintext seed resulting
|
||||
// from deciphering the cipher seed. The size consists of the
|
||||
// following:
|
||||
//
|
||||
// * 1 byte version || 2 bytes timestamp || 16 bytes of entropy.
|
||||
//
|
||||
// The version is used by wallets to know how to re-derive relevant
|
||||
// addresses, the 2 byte timestamp a BDG (Bitcoin Days Genesis) offset,
|
||||
// and finally, the 16 bytes to be used to generate the HD wallet seed.
|
||||
DecipheredCipherSeedSize = 19
|
||||
|
||||
// EncipheredCipherSeedSize is the size of the fully encoded+enciphered
|
||||
// cipher seed. We first obtain the enciphered plaintext seed by
|
||||
// carrying out the enciphering as governed in the current version. We
|
||||
// then take that enciphered seed (now 19+4=23 bytes due to ciphertext
|
||||
// expansion, essentially a checksum) and prepend a version, then
|
||||
// append the salt, and then take a checksum of everything. The
|
||||
// checksum allows us to verify that the user input the correct set of
|
||||
// words, then we can verify the passphrase due to the internal MAC
|
||||
// equiv. The final breakdown is:
|
||||
//
|
||||
// * 1 byte version || 23 byte enciphered seed || 5 byte salt || 4 byte checksum
|
||||
//
|
||||
// With CipherSeedVersion we encipher as follows: we use
|
||||
// scrypt(n=32768, r=8, p=1) to derive a 32-byte key from an optional
|
||||
// user passphrase. We then encipher the plaintext seed using a value
|
||||
// of tau (with aez) of 8-bytes (so essentially a 32-bit MAC). When
|
||||
// enciphering, we include the version and scrypt salt as the AD. This
|
||||
// gives us a total of 33 bytes. These 33 bytes fit cleanly into 24
|
||||
// mnemonic words.
|
||||
EncipheredCipherSeedSize = 33
|
||||
|
||||
// CipherTextExpansion is the number of bytes that will be added as
|
||||
// redundancy for the enciphering scheme implemented by aez. This can
|
||||
// be seen as the size of the equivalent MAC.
|
||||
CipherTextExpansion = 4
|
||||
|
||||
// EntropySize is the number of bytes of entropy we'll use the generate
|
||||
// the seed.
|
||||
EntropySize = 16
|
||||
|
||||
// NummnemonicWords is the number of words that an encoded cipher seed
|
||||
// will result in.
|
||||
NummnemonicWords = 24
|
||||
|
||||
// saltSize is the size of the salt we'll generate to use with scrypt
|
||||
// to generate a key for use within aez from the user's passphrase. The
|
||||
// role of the salt is to make the creation of rainbow tables
|
||||
// infeasible.
|
||||
saltSize = 5
|
||||
|
||||
// adSize is the size of the encoded associated data that will be
|
||||
// passed into aez when enciphering and deciphering the seed. The AD
|
||||
// itself (associated data) is just the CipherSeedVersion and salt.
|
||||
adSize = 6
|
||||
|
||||
// checkSumSize is the size of the checksum applied to the final
|
||||
// encoded ciphertext.
|
||||
checkSumSize = 4
|
||||
|
||||
// keyLen is the size of the key that we'll use for encryption with
|
||||
// aez.
|
||||
keyLen = 32
|
||||
|
||||
// bitsPerWord is the number of bits each word in the wordlist encodes.
|
||||
// We encode our mnemonic using 24 words, so 264 bits (33 bytes).
|
||||
bitsPerWord = 11
|
||||
|
||||
// saltOffset is the index within an enciphered cipherseed that marks
|
||||
// the start of the salt.
|
||||
saltOffset = EncipheredCipherSeedSize - checkSumSize - saltSize
|
||||
|
||||
// checkSumSize is the index within an enciphered cipher seed that
|
||||
// marks the start of the checksum.
|
||||
checkSumOffset = EncipheredCipherSeedSize - checkSumSize
|
||||
|
||||
// encipheredSeedSize is the size of the cipherseed before applying the
|
||||
// external version, salt, and checksum for the final encoding.
|
||||
encipheredSeedSize = DecipheredCipherSeedSize + CipherTextExpansion
|
||||
)
|
||||
|
||||
var (
|
||||
// Below at the default scrypt parameters that are tied to
|
||||
// CipherSeedVersion zero.
|
||||
scryptN = 32768
|
||||
scryptR = 8
|
||||
scryptP = 1
|
||||
|
||||
// crcTable is a table that presents the polynomial we'll use for
|
||||
// computing our checksum.
|
||||
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
|
||||
// defaultPassphras is the default passphrase that will be used for
|
||||
// encryption in the case that the user chooses not to specify their
|
||||
// own passphrase.
|
||||
defaultPassphrase = []byte("aezeed")
|
||||
)
|
||||
|
||||
var (
|
||||
// bitcoinGenesisDate is the timestamp of Bitcoin's genesis block.
|
||||
// We'll use this value in order to create a compact birthday for the
|
||||
// seed. The birthday will be interested as the number of days since
|
||||
// the genesis date. We refer to this time period as ABE (after Bitcoin
|
||||
// era).
|
||||
bitcoinGenesisDate = time.Unix(1231006505, 0)
|
||||
)
|
||||
|
||||
// CipherSeed is a fully decoded instance of the aezeed scheme. At a high
|
||||
// level, the encoded cipherseed is the enciphering of: a version byte, a set
|
||||
// of bytes for a timestamp, the entropy which will be used to directly
|
||||
// construct the HD seed, and finally a checksum over the rest. This scheme was
|
||||
// created as the widely used schemes in the space lack two critical traits: a
|
||||
// version byte, and a birthday timestamp. The version allows us to modify the
|
||||
// details of the scheme in the future, and the birthday gives wallets a limit
|
||||
// of how far back in the chain they'll need to start scanning. We also add an
|
||||
// external version to the enciphering plaintext seed. With this addition,
|
||||
// seeds are able to be "upgraded" (to diff params, or entirely diff crypt),
|
||||
// while maintaining the semantics of the plaintext seed.
|
||||
//
|
||||
// The core of the scheme is the usage of aez to carefully control the size of
|
||||
// the final encrypted seed. With the current parameters, this scheme can be
|
||||
// encoded using a 24 word mnemonic. We use 4 bytes of ciphertext expansion
|
||||
// when enciphering the raw seed, giving us the equivalent of 40-bit MAC (as we
|
||||
// check for a particular seed version). Using the external 4 byte checksum,
|
||||
// we're able to ensure that the user input the correct set of words. Finally,
|
||||
// the password in the scheme is optional. If not specified, "aezeed" will be
|
||||
// used as the password. Otherwise, the addition of the password means that
|
||||
// users can encrypt the raw "plaintext" seed under distinct passwords to
|
||||
// produce unique mnemonic phrases.
|
||||
type CipherSeed struct {
|
||||
// InternalVersion is the version of the plaintext cipherseed. This is
|
||||
// to be used by wallets to determine if the seed version is compatible
|
||||
// with the derivation schemes they know.
|
||||
InternalVersion uint8
|
||||
|
||||
// Birthday is the time that the seed was created. This is expressed as
|
||||
// the number of days since the timestamp in the Bitcoin genesis block.
|
||||
// We use days as seconds gives us wasted granularity. The oldest seed
|
||||
// that we can encode using this format is through the date 2188.
|
||||
Birthday uint16
|
||||
|
||||
// Entropy is a set of bytes generated via a CSPRNG. This is the value
|
||||
// that should be used to directly generate the HD root, as defined
|
||||
// within BIP0032.
|
||||
Entropy [EntropySize]byte
|
||||
|
||||
// salt is the salt that was used to generate the key from the user's
|
||||
// specified passphrase.
|
||||
salt [saltSize]byte
|
||||
}
|
||||
|
||||
// New generates a new CipherSeed instance from an optional source of entropy.
|
||||
// If the entropy isn't provided, then a set of random bytes will be used in
|
||||
// place. The final argument should be the time at which the seed was created.
|
||||
func New(internalVersion uint8, entropy *[EntropySize]byte,
|
||||
now time.Time) (*CipherSeed, error) {
|
||||
|
||||
// TODO(roasbeef): pass randomness source? to make fully determinsitc?
|
||||
|
||||
// If a set of entropy wasn't provided, then we'll read a set of bytes
|
||||
// from the CSPRNG of our operating platform.
|
||||
var seed [EntropySize]byte
|
||||
if entropy == nil {
|
||||
if _, err := rand.Read(seed[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we'll copy the set of bytes.
|
||||
copy(seed[:], entropy[:])
|
||||
}
|
||||
|
||||
// To compute our "birthday", we'll first use the current time, then
|
||||
// subtract that from the Bitcoin Genesis Date. We'll then convert that
|
||||
// value to days.
|
||||
birthday := uint16(now.Sub(bitcoinGenesisDate) / (time.Hour * 24))
|
||||
|
||||
c := &CipherSeed{
|
||||
InternalVersion: internalVersion,
|
||||
Birthday: birthday,
|
||||
Entropy: seed,
|
||||
}
|
||||
|
||||
// Next, we'll read a random salt that will be used with scrypt to
|
||||
// eventually derive our key.
|
||||
if _, err := rand.Read(c.salt[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// encode attempts to encode the target cipherSeed into the passed io.Writer
|
||||
// instance.
|
||||
func (c *CipherSeed) encode(w io.Writer) error {
|
||||
err := binary.Write(w, binary.BigEndian, c.InternalVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Write(w, binary.BigEndian, c.Birthday); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(c.Entropy[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decode attempts to decode an encoded cipher seed instance into the target
|
||||
// CipherSeed struct.
|
||||
func (c *CipherSeed) decode(r io.Reader) error {
|
||||
err := binary.Read(r, binary.BigEndian, &c.InternalVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &c.Birthday); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, c.Entropy[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeAD returns the fully encoded associated data for use when performing
|
||||
// our current enciphering operation. The AD is: version || salt.
|
||||
func encodeAD(version uint8, salt [saltSize]byte) [adSize]byte {
|
||||
var ad [adSize]byte
|
||||
ad[0] = byte(version)
|
||||
copy(ad[1:], salt[:])
|
||||
|
||||
return ad
|
||||
}
|
||||
|
||||
// extractAD extracts an associated data from a fully encoded and enciphered
|
||||
// cipher seed. This is to be used when attempting to decrypt an enciphered
|
||||
// cipher seed.
|
||||
func extractAD(encipheredSeed [EncipheredCipherSeedSize]byte) [adSize]byte {
|
||||
var ad [adSize]byte
|
||||
ad[0] = encipheredSeed[0]
|
||||
|
||||
copy(ad[1:], encipheredSeed[saltOffset:checkSumOffset])
|
||||
|
||||
return ad
|
||||
}
|
||||
|
||||
// encipher takes a fully populated cipherseed instance, and enciphers the
|
||||
// encoded seed, then appends a randomly generated seed used to stretch the
|
||||
// passphrase out into an appropriate key, then computes a checksum over the
|
||||
// preceding.
|
||||
func (c *CipherSeed) encipher(pass []byte) ([EncipheredCipherSeedSize]byte, error) {
|
||||
var cipherSeedBytes [EncipheredCipherSeedSize]byte
|
||||
|
||||
// If the passphrase wasn't provided, then we'll use the string
|
||||
// "aezeed" in place.
|
||||
passphrase := pass
|
||||
if len(passphrase) == 0 {
|
||||
passphrase = defaultPassphrase
|
||||
}
|
||||
|
||||
// With our salt pre-generated, we'll now run the password through a
|
||||
// KDF to obtain the key we'll use for encryption.
|
||||
key, err := scrypt.Key(
|
||||
passphrase, c.salt[:], scryptN, scryptR, scryptP, keyLen,
|
||||
)
|
||||
if err != nil {
|
||||
return cipherSeedBytes, err
|
||||
}
|
||||
|
||||
// Next, we'll encode the serialized plaintext cipherseed into a buffer
|
||||
// that we'll use for encryption.
|
||||
var seedBytes bytes.Buffer
|
||||
if err := c.encode(&seedBytes); err != nil {
|
||||
return cipherSeedBytes, err
|
||||
}
|
||||
|
||||
// With our plaintext seed encoded, we'll now construct the AD that
|
||||
// will be passed to the encryption operation. This ensures to
|
||||
// authenticate both the salt and the external version.
|
||||
ad := encodeAD(CipherSeedVersion, c.salt)
|
||||
|
||||
// With all items assembled, we'll now encipher the plaintext seed
|
||||
// with our AD, key, and MAC size.
|
||||
cipherSeed := seedBytes.Bytes()
|
||||
cipherText := aez.Encrypt(
|
||||
key, nil, [][]byte{ad[:]}, CipherTextExpansion, cipherSeed, nil,
|
||||
)
|
||||
|
||||
// Finally, we'll pack the {version || ciphertext || salt || checksum}
|
||||
// seed into a byte slice for encoding as a mnemonic.
|
||||
cipherSeedBytes[0] = byte(CipherSeedVersion)
|
||||
copy(cipherSeedBytes[1:saltOffset], cipherText)
|
||||
copy(cipherSeedBytes[saltOffset:], c.salt[:])
|
||||
|
||||
// With the seed mostly assembled, we'll now compute a checksum all the
|
||||
// contents.
|
||||
checkSum := crc32.Checksum(cipherSeedBytes[:checkSumOffset], crcTable)
|
||||
|
||||
// With our checksum computed, we can finish encoding the full cipher
|
||||
// seed.
|
||||
var checkSumBytes [4]byte
|
||||
binary.BigEndian.PutUint32(checkSumBytes[:], checkSum)
|
||||
copy(cipherSeedBytes[checkSumOffset:], checkSumBytes[:])
|
||||
|
||||
return cipherSeedBytes, nil
|
||||
}
|
||||
|
||||
// cipherTextToMnemonic converts the aez ciphertext appended with the salt to a
|
||||
// 24-word mnemonic pass phrase.
|
||||
func cipherTextToMnemonic(cipherText [EncipheredCipherSeedSize]byte) (Mnemonic, error) {
|
||||
var words [NummnemonicWords]string
|
||||
|
||||
// First, we'll convert the ciphertext itself into a bitstream for easy
|
||||
// manipulation.
|
||||
cipherBits := bstream.NewBStreamReader(cipherText[:])
|
||||
|
||||
// With our bitstream obtained, we'll read 11 bits at a time, then use
|
||||
// that to index into our word list to obtain the next word.
|
||||
for i := 0; i < NummnemonicWords; i++ {
|
||||
index, err := cipherBits.ReadBits(bitsPerWord)
|
||||
if err != nil {
|
||||
return words, nil
|
||||
}
|
||||
|
||||
words[i] = defaultWordList[index]
|
||||
}
|
||||
|
||||
return words, nil
|
||||
}
|
||||
|
||||
// ToMnemonic maps the final enciphered cipher seed to a human readable 24-word
|
||||
// mnemonic phrase. The password is optional, as if it isn't specified aezeed
|
||||
// will be used in its place.
|
||||
func (c *CipherSeed) ToMnemonic(pass []byte) (Mnemonic, error) {
|
||||
// First, we'll convert the valid seed triple into an aez cipher text
|
||||
// with our KDF salt appended to it.
|
||||
cipherText, err := c.encipher(pass)
|
||||
if err != nil {
|
||||
return Mnemonic{}, nil
|
||||
}
|
||||
|
||||
// Now that we have our cipher text, we'll convert it into a mnemonic
|
||||
// phrase.
|
||||
return cipherTextToMnemonic(cipherText)
|
||||
}
|
||||
|
||||
// Encipher maps the cipher seed to an aez ciphertext using an optional
|
||||
// passphrase.
|
||||
func (c *CipherSeed) Encipher(pass []byte) ([EncipheredCipherSeedSize]byte, error) {
|
||||
return c.encipher(pass)
|
||||
}
|
||||
|
||||
// Mnemonic is a 24-word passphrase as of CipherSeedVersion zero. This
|
||||
// passphrase encodes an encrypted seed triple (version, birthday, entropy).
|
||||
// Additionally, we also encode the salt used with scrypt to derive the key
|
||||
// that the cipher text is encrypted with, and the version which tells us how
|
||||
// to decipher the seed.
|
||||
type Mnemonic [NummnemonicWords]string
|
||||
|
||||
// mnemonicToCipherText converts a 24-word mnemonic phrase into a 33 byte
|
||||
// cipher text.
|
||||
//
|
||||
// NOTE: This assumes that all words have already been checked to be amongst
|
||||
// our word list.
|
||||
func mnemonicToCipherText(mnemonic *Mnemonic) [EncipheredCipherSeedSize]byte {
|
||||
var cipherText [EncipheredCipherSeedSize]byte
|
||||
|
||||
// We'll now perform the reverse mapping to that of
|
||||
// cipherTextToMnemonic: we'll get the index of the word, then write
|
||||
// out that index to the bit stream.
|
||||
cipherBits := bstream.NewBStreamWriter(EncipheredCipherSeedSize)
|
||||
for _, word := range mnemonic {
|
||||
// Using the reverse word map, we'll locate the index of this
|
||||
// word within the word list.
|
||||
index := uint64(reverseWordMap[word])
|
||||
|
||||
// With the index located, we'll now write this out to the
|
||||
// bitstream, appending to what's already there.
|
||||
cipherBits.WriteBits(index, bitsPerWord)
|
||||
}
|
||||
|
||||
copy(cipherText[:], cipherBits.Bytes())
|
||||
|
||||
return cipherText
|
||||
}
|
||||
|
||||
// ToCipherSeed attempts to map the mnemonic to the original cipher text byte
|
||||
// slice. Then we'll attempt to decrypt the ciphertext using aez with the
|
||||
// passed passphrase, using the last 5 bytes of the ciphertext as a salt for
|
||||
// the KDF.
|
||||
func (m *Mnemonic) ToCipherSeed(pass []byte) (*CipherSeed, error) {
|
||||
// First, we'll attempt to decipher the mnemonic by mapping back into
|
||||
// our byte slice and applying our deciphering scheme.
|
||||
plainSeed, err := m.Decipher(pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If decryption was successful, then we'll decode into a fresh
|
||||
// CipherSeed struct.
|
||||
var c CipherSeed
|
||||
if err := c.decode(bytes.NewReader(plainSeed[:])); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// decipherCipherSeed attempts to decipher the passed cipher seed ciphertext
|
||||
// using the passed passphrase. This function is the opposite of
|
||||
// the encipher method.
|
||||
func decipherCipherSeed(cipherSeedBytes [EncipheredCipherSeedSize]byte,
|
||||
pass []byte) ([DecipheredCipherSeedSize]byte, error) {
|
||||
|
||||
var plainSeed [DecipheredCipherSeedSize]byte
|
||||
|
||||
// Before we do anything, we'll ensure that the version is one that we
|
||||
// understand. Otherwise, we won't be able to decrypt, or even parse
|
||||
// the cipher seed.
|
||||
if uint8(cipherSeedBytes[0]) != CipherSeedVersion {
|
||||
return plainSeed, ErrIncorrectVersion
|
||||
}
|
||||
|
||||
// Next, we'll slice off the salt from the pass cipher seed, then
|
||||
// snip off the end of the cipher seed, ignoring the version, and
|
||||
// finally the checksum.
|
||||
salt := cipherSeedBytes[saltOffset : saltOffset+saltSize]
|
||||
cipherSeed := cipherSeedBytes[1:saltOffset]
|
||||
checksum := cipherSeedBytes[checkSumOffset:]
|
||||
|
||||
// Before we perform any crypto operations, we'll re-create and verify
|
||||
// the checksum to ensure that the user input the proper set of words.
|
||||
freshChecksum := crc32.Checksum(cipherSeedBytes[:checkSumOffset], crcTable)
|
||||
if freshChecksum != binary.BigEndian.Uint32(checksum) {
|
||||
return plainSeed, ErrIncorrectMnemonic
|
||||
}
|
||||
|
||||
// With the salt separated from the cipher text, we'll now obtain the
|
||||
// key used for encryption.
|
||||
key, err := scrypt.Key(pass, salt, scryptN, scryptR, scryptP, keyLen)
|
||||
if err != nil {
|
||||
return plainSeed, err
|
||||
}
|
||||
|
||||
// We'll also extract the AD that will be required to properly pass the
|
||||
// MAC check.
|
||||
ad := extractAD(cipherSeedBytes)
|
||||
|
||||
// With the key, we'll attempt to decrypt the plaintext. If the
|
||||
// ciphertext was altered, or the passphrase is incorrect, then we'll
|
||||
// error out.
|
||||
plainSeedBytes, ok := aez.Decrypt(
|
||||
key, nil, [][]byte{ad[:]}, CipherTextExpansion, cipherSeed, nil,
|
||||
)
|
||||
if !ok {
|
||||
return plainSeed, ErrInvalidPass
|
||||
}
|
||||
copy(plainSeed[:], plainSeedBytes)
|
||||
|
||||
return plainSeed, nil
|
||||
|
||||
}
|
||||
|
||||
// Decipher attempts to decipher the encoded mnemonic by first mapping to the
|
||||
// original chipertext, then applying our deciphering scheme. ErrInvalidPass
|
||||
// will be returned if the passphrase is incorrect.
|
||||
func (m *Mnemonic) Decipher(pass []byte) ([DecipheredCipherSeedSize]byte, error) {
|
||||
|
||||
// Before we attempt to map the mnemonic back to the original
|
||||
// ciphertext, we'll ensure that all the word are actually a part of
|
||||
// the current default word list.
|
||||
for _, word := range m {
|
||||
if !strings.Contains(englishWordList, word) {
|
||||
emptySeed := [DecipheredCipherSeedSize]byte{}
|
||||
return emptySeed, ErrUnknownMnenomicWord{word}
|
||||
}
|
||||
}
|
||||
|
||||
// If the passphrase wasn't provided, then we'll use the string
|
||||
// "aezeed" in place.
|
||||
passphrase := pass
|
||||
if len(passphrase) == 0 {
|
||||
passphrase = defaultPassphrase
|
||||
}
|
||||
|
||||
// Next, we'll map the mnemonic phrase back into the original cipher
|
||||
// text.
|
||||
cipherText := mnemonicToCipherText(m)
|
||||
|
||||
// Finally, we'll attempt to decipher the enciphered seed. The result
|
||||
// will be the raw seed minus the ciphertext expansion, external
|
||||
// version, and salt.
|
||||
return decipherCipherSeed(cipherText, passphrase)
|
||||
}
|
||||
|
||||
// ChangePass takes an existing mnemonic, and passphrase for said mnemonic and
|
||||
// re-enciphers the plaintext cipher seed into a brand new mnemonic. This can
|
||||
// be used to allow users to re-encrypt the same seed with multiple pass
|
||||
// phrases, or just change the passphrase on an existing seed.
|
||||
func (m *Mnemonic) ChangePass(oldPass, newPass []byte) (Mnemonic, error) {
|
||||
var newmnemonic Mnemonic
|
||||
|
||||
// First, we'll try to decrypt the current mnemonic using the existing
|
||||
// passphrase. If this fails, then we can't proceed any further.
|
||||
cipherSeed, err := m.ToCipherSeed(oldPass)
|
||||
if err != nil {
|
||||
return newmnemonic, err
|
||||
}
|
||||
|
||||
// If the deciperhing was successful, then we'll now re-encipher using
|
||||
// the new user provided passphrase.
|
||||
return cipherSeed.ToMnemonic(newPass)
|
||||
}
|
513
aezeed/cipherseeed_test.go
Normal file
513
aezeed/cipherseeed_test.go
Normal file
@ -0,0 +1,513 @@
|
||||
package aezeed
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
testEntropy = [EntropySize]byte{
|
||||
0x81, 0xb6, 0x37, 0xd8,
|
||||
0x63, 0x59, 0xe6, 0x96,
|
||||
0x0d, 0xe7, 0x95, 0xe4,
|
||||
0x1e, 0x0b, 0x4c, 0xfd,
|
||||
}
|
||||
)
|
||||
|
||||
func assertCipherSeedEqual(t *testing.T, cipherSeed *CipherSeed,
|
||||
cipherSeed2 *CipherSeed) {
|
||||
|
||||
if cipherSeed.InternalVersion != cipherSeed2.InternalVersion {
|
||||
t.Fatalf("mismatched versions: expected %v, got %v",
|
||||
cipherSeed.InternalVersion, cipherSeed2.InternalVersion)
|
||||
}
|
||||
if cipherSeed.Birthday != cipherSeed2.Birthday {
|
||||
t.Fatalf("mismatched birthday: expected %v, got %v",
|
||||
cipherSeed.Birthday, cipherSeed2.Birthday)
|
||||
}
|
||||
if cipherSeed.Entropy != cipherSeed2.Entropy {
|
||||
t.Fatalf("mismatched versions: expected %x, got %x",
|
||||
cipherSeed.Entropy[:], cipherSeed2.Entropy[:])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAezeedVersion0TestVectors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// TODO(roasbeef):
|
||||
}
|
||||
|
||||
// TestEmptyPassphraseDerivation tests that the aezeed scheme is able to derive
|
||||
// a proper mnemonic, and decipher that mnemonic when the user uses an empty
|
||||
// passphrase.
|
||||
func TestEmptyPassphraseDerivation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Our empty passphrase...
|
||||
pass := []byte{}
|
||||
|
||||
// We'll now create a new cipher seed with an internal version of zero
|
||||
// to simulate a wallet that just adopted the scheme.
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that the seed has been created, we'll attempt to convert it to a
|
||||
// valid mnemonic.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Next, we'll try to decrypt the mnemonic with the passphrase that we
|
||||
// used.
|
||||
cipherSeed2, err := mnemonic.ToCipherSeed(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decrypt mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Finally, we'll ensure that the uncovered cipher seed matches
|
||||
// precisely.
|
||||
assertCipherSeedEqual(t, cipherSeed, cipherSeed2)
|
||||
}
|
||||
|
||||
// TestManualEntropyGeneration tests that if the user doesn't provide a source
|
||||
// of entropy, then we do so ourselves.
|
||||
func TestManualEntropyGeneration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Our empty passphrase...
|
||||
pass := []byte{}
|
||||
|
||||
// We'll now create a new cipher seed with an internal version of zero
|
||||
// to simulate a wallet that just adopted the scheme.
|
||||
cipherSeed, err := New(0, nil, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that the seed has been created, we'll attempt to convert it to a
|
||||
// valid mnemonic.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Next, we'll try to decrypt the mnemonic with the passphrase that we
|
||||
// used.
|
||||
cipherSeed2, err := mnemonic.ToCipherSeed(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decrypt mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Finally, we'll ensure that the uncovered cipher seed matches
|
||||
// precisely.
|
||||
assertCipherSeedEqual(t, cipherSeed, cipherSeed2)
|
||||
}
|
||||
|
||||
// TestInvalidPassphraseRejection tests if a caller attempts to use the
|
||||
// incorrect passprhase for an enciphered seed, then the proper error is
|
||||
// returned.
|
||||
func TestInvalidPassphraseRejection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First, we'll generate a new cipher seed with a test passphrase.
|
||||
pass := []byte("test")
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have our cipher seed, we'll encipher it and request a
|
||||
// mnemonic that we can use to recover later.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// If we try to decipher with the wrong passphrase, we should get the
|
||||
// proper error.
|
||||
wrongPass := []byte("kek")
|
||||
if _, err := mnemonic.ToCipherSeed(wrongPass); err != ErrInvalidPass {
|
||||
t.Fatalf("expected ErrInvalidPass, instead got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRawEncipherDecipher tests that callers are able to use the raw methods
|
||||
// to map between ciphertext and the raw plaintext deciphered seed.
|
||||
func TestRawEncipherDecipher(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First, we'll generate a new cipher seed with a test passphrase.
|
||||
pass := []byte("test")
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// With the cipherseed obtained, we'll now use the raw encipher method
|
||||
// to obtain our final cipher text.
|
||||
cipherText, err := cipherSeed.Encipher(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to encipher seed: %v", err)
|
||||
}
|
||||
|
||||
mnemonic, err := cipherTextToMnemonic(cipherText)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have the ciphertext (mapped to the mnemonic), we'll
|
||||
// attempt to decipher it raw using the user's passphrase.
|
||||
plainSeedBytes, err := mnemonic.Decipher(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decipher: %v", err)
|
||||
}
|
||||
|
||||
// If we deserialize the plaintext seed bytes, it should exactly match
|
||||
// the original cipher seed.
|
||||
var newSeed CipherSeed
|
||||
err = newSeed.decode(bytes.NewReader(plainSeedBytes[:]))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decode cipher seed: %v", err)
|
||||
}
|
||||
|
||||
assertCipherSeedEqual(t, cipherSeed, &newSeed)
|
||||
}
|
||||
|
||||
// TestInvalidExternalVersion tests that if we present a ciphertext with the
|
||||
// incorrect version to decipherCipherSeed, then it fails with the expected
|
||||
// error.
|
||||
func TestInvalidExternalVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First, we'll generate a new cipher seed.
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// With the cipherseed obtained, we'll now use the raw encipher method
|
||||
// to obtain our final cipher text.
|
||||
pass := []byte("newpasswhodis")
|
||||
cipherText, err := cipherSeed.Encipher(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to encipher seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have the cipher text, we'll modify the first byte to be
|
||||
// an invalid version.
|
||||
cipherText[0] = 44
|
||||
|
||||
// With the version swapped, if we try to decipher it, (no matter the
|
||||
// passphrase), it should fail.
|
||||
_, err = decipherCipherSeed(cipherText, []byte("kek"))
|
||||
if err != ErrIncorrectVersion {
|
||||
t.Fatalf("wrong error: expected ErrIncorrectVersion, "+
|
||||
"got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestChangePassphrase tests that we're able to generate a cipher seed, then
|
||||
// change the password. If we attempt to decipher the new enciphered seed, then
|
||||
// we should get the exact same seed back.
|
||||
func TestChangePassphrase(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First, we'll generate a new cipher seed with a test passphrase.
|
||||
pass := []byte("test")
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have our cipher seed, we'll encipher it and request a
|
||||
// mnemonic that we can use to recover later.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Now that have the mnemonic, we'll attempt to re-encipher the
|
||||
// passphrase in order to get a brand new mnemonic.
|
||||
newPass := []byte("strongerpassyeh!")
|
||||
newmnemonic, err := mnemonic.ChangePass(pass, newPass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to change passphrase: %v", err)
|
||||
}
|
||||
|
||||
// We'll now attempt to decipher the new mnemonic using the new
|
||||
// passphrase to arrive at (what should be) the original cipher seed.
|
||||
newCipherSeed, err := newmnemonic.ToCipherSeed(newPass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decipher cipher seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have the cipher seed, we'll verify that the plaintext
|
||||
// seed matches *identically*.
|
||||
assertCipherSeedEqual(t, cipherSeed, newCipherSeed)
|
||||
}
|
||||
|
||||
// TestChangePassphraseWrongPass tests that if we have a valid enciphered
|
||||
// cipherseed, but then try to change the password with the *wrong* password,
|
||||
// then we get an error.
|
||||
func TestChangePassphraseWrongPass(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First, we'll generate a new cipher seed with a test passphrase.
|
||||
pass := []byte("test")
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have our cipher seed, we'll encipher it and request a
|
||||
// mnemonic that we can use to recover later.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Now that have the mnemonic, we'll attempt to re-encipher the
|
||||
// passphrase in order to get a brand new mnemonic. However, we'll be
|
||||
// using the *wrong* passphrase. This should result in an
|
||||
// ErrInvalidPass error.
|
||||
wrongPass := []byte("kek")
|
||||
newPass := []byte("strongerpassyeh!")
|
||||
_, err = mnemonic.ChangePass(wrongPass, newPass)
|
||||
if err != ErrInvalidPass {
|
||||
t.Fatalf("expected ErrInvalidPass, instead got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMnemonicEncoding uses quickcheck like property based testing to ensure
|
||||
// that we're always able to fully recover the original byte stream encoded
|
||||
// into the mnemonic phrase.
|
||||
func TestMnemonicEncoding(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// mainScenario is the main driver of our property based test. We'll
|
||||
// ensure that given a random byte string of length 33 bytes, if we
|
||||
// convert that to the mnemonic, then we should be able to reverse the
|
||||
// conversion.
|
||||
mainScenario := func(cipherSeedBytes [EncipheredCipherSeedSize]byte) bool {
|
||||
mnemonic, err := cipherTextToMnemonic(cipherSeedBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to map cipher text: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
newCipher := mnemonicToCipherText(&mnemonic)
|
||||
|
||||
if newCipher != cipherSeedBytes {
|
||||
t.Fatalf("cipherseed doesn't match: expected %v, got %v",
|
||||
cipherSeedBytes, newCipher)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(mainScenario, nil); err != nil {
|
||||
t.Fatalf("fuzz check failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEncipherDecipher is a property-based test that ensures that given a
|
||||
// version, entropy, and birthday, then we're able to map that to a cipherseed
|
||||
// mnemonic, then back to the original plaintext cipher seed.
|
||||
func TestEncipherDecipher(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// mainScenario is the main driver of our property based test. We'll
|
||||
// ensure that given a random seed tuple (internal version, entropy,
|
||||
// and birthday) we're able to convert that to a valid cipher seed.
|
||||
// Additionally, we should be able to decipher the final mnemonic, and
|
||||
// recover the original cipherseed.
|
||||
mainScenario := func(version uint8, entropy [EntropySize]byte,
|
||||
nowInt int64, pass [20]byte) bool {
|
||||
|
||||
now := time.Unix(nowInt, 0)
|
||||
|
||||
cipherSeed, err := New(version, &entropy, now)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to map cipher text: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass[:])
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate mnemonic: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
cipherSeed2, err := mnemonic.ToCipherSeed(pass[:])
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decrypt cipher seed: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if cipherSeed.InternalVersion != cipherSeed2.InternalVersion {
|
||||
t.Fatalf("mismatched versions: expected %v, got %v",
|
||||
cipherSeed.InternalVersion, cipherSeed2.InternalVersion)
|
||||
return false
|
||||
}
|
||||
if cipherSeed.Birthday != cipherSeed2.Birthday {
|
||||
t.Fatalf("mismatched birthday: expected %v, got %v",
|
||||
cipherSeed.Birthday, cipherSeed2.Birthday)
|
||||
return false
|
||||
}
|
||||
if cipherSeed.Entropy != cipherSeed2.Entropy {
|
||||
t.Fatalf("mismatched versions: expected %x, got %x",
|
||||
cipherSeed.Entropy[:], cipherSeed2.Entropy[:])
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(mainScenario, nil); err != nil {
|
||||
t.Fatalf("fuzz check failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSeedEncodeDecode tests that we're able to reverse the encoding of an
|
||||
// arbitrary raw seed.
|
||||
func TestSeedEncodeDecode(t *testing.T) {
|
||||
// mainScenario is the primary driver of our property-based test. We'll
|
||||
// ensure that given a random cipher seed, we can encode it an decode
|
||||
// it precisely.
|
||||
mainScenario := func(version uint8, nowInt int64,
|
||||
entropy [EntropySize]byte) bool {
|
||||
|
||||
now := time.Unix(nowInt, 0)
|
||||
seed := CipherSeed{
|
||||
InternalVersion: version,
|
||||
Birthday: uint16(now.Sub(bitcoinGenesisDate) / (time.Hour * 24)),
|
||||
Entropy: entropy,
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
if err := seed.encode(&b); err != nil {
|
||||
t.Fatalf("unable to encode: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
var newSeed CipherSeed
|
||||
if err := newSeed.decode(&b); err != nil {
|
||||
t.Fatalf("unable to decode: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if seed.InternalVersion != newSeed.InternalVersion {
|
||||
t.Fatalf("mismatched versions: expected %v, got %v",
|
||||
seed.InternalVersion, newSeed.InternalVersion)
|
||||
return false
|
||||
}
|
||||
if seed.Birthday != newSeed.Birthday {
|
||||
t.Fatalf("mismatched birthday: expected %v, got %v",
|
||||
seed.Birthday, newSeed.Birthday)
|
||||
return false
|
||||
}
|
||||
if seed.Entropy != newSeed.Entropy {
|
||||
t.Fatalf("mismatched versions: expected %x, got %x",
|
||||
seed.Entropy[:], newSeed.Entropy[:])
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(mainScenario, nil); err != nil {
|
||||
t.Fatalf("fuzz check failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecipherUnknownMnenomicWord tests that if we obtain a mnemonic, the
|
||||
// modify one of the words to not be within the word list, then it's detected
|
||||
// when we attempt to map it back to the original cipher seed.
|
||||
func TestDecipherUnknownMnenomicWord(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First, we'll create a new cipher seed with "test" ass a password.
|
||||
pass := []byte("test")
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have our cipher seed, we'll encipher it and request a
|
||||
// mnemonic that we can use to recover later.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Before we attempt to decrypt the cipher seed, we'll mutate one of
|
||||
// the word so it isn't actually in our final word list.
|
||||
randIndex := rand.Int31n(int32(len(mnemonic)))
|
||||
mnemonic[randIndex] = "kek"
|
||||
|
||||
// If we attempt to map back to the original cipher seed now, then we
|
||||
// should get ErrUnknownMnenomicWord.
|
||||
_, err = mnemonic.ToCipherSeed(pass)
|
||||
if err == nil {
|
||||
t.Fatalf("expected ErrUnknownMnenomicWord error")
|
||||
}
|
||||
|
||||
wordErr, ok := err.(ErrUnknownMnenomicWord)
|
||||
if !ok {
|
||||
t.Fatalf("expected ErrUnknownMnenomicWord instead got %T", err)
|
||||
}
|
||||
|
||||
if wordErr.word != "kek" {
|
||||
t.Fatalf("word mismatch: expected %v, got %v", "kek", wordErr.word)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecipherIncorrectMnemonic tests that if we obtain a cipherseed, but then
|
||||
// swap out words, then checksum fails.
|
||||
func TestDecipherIncorrectMnemonic(t *testing.T) {
|
||||
// First, we'll create a new cipher seed with "test" ass a password.
|
||||
pass := []byte("test")
|
||||
cipherSeed, err := New(0, &testEntropy, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create seed: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have our cipher seed, we'll encipher it and request a
|
||||
// mnemonic that we can use to recover later.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(pass)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// We'll now swap out two words from the mnemonic, which should trigger
|
||||
// a checksum failure.
|
||||
swapIndex1 := 9
|
||||
swapIndex2 := 13
|
||||
mnemonic[swapIndex1], mnemonic[swapIndex2] = mnemonic[swapIndex2], mnemonic[swapIndex1]
|
||||
|
||||
// If we attempt to decrypt now, we should get a checksum failure.
|
||||
// If we attempt to map back to the original cipher seed now, then we
|
||||
// should get ErrUnknownMnenomicWord.
|
||||
_, err = mnemonic.ToCipherSeed(pass)
|
||||
if err != ErrIncorrectMnemonic {
|
||||
t.Fatalf("expected ErrIncorrectMnemonic error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO(roasbeef): add test failure checksum fail is modified, new error
|
||||
|
||||
func init() {
|
||||
// For the purposes of our test, we'll crank down the scrypt params a
|
||||
// bit.
|
||||
scryptN = 16
|
||||
scryptR = 8
|
||||
scryptP = 1
|
||||
}
|
30
aezeed/errors.go
Normal file
30
aezeed/errors.go
Normal file
@ -0,0 +1,30 @@
|
||||
package aezeed
|
||||
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
// ErrIncorrectVersion is returned if a seed bares a mismatched
|
||||
// external version to that of the package executing the aezeed scheme.
|
||||
ErrIncorrectVersion = fmt.Errorf("wrong seed version")
|
||||
|
||||
// ErrInvalidPass is returned if the user enters an invalid passphrase
|
||||
// for a particular enciphered mnemonic.
|
||||
ErrInvalidPass = fmt.Errorf("invalid passphrase")
|
||||
|
||||
// ErrIncorrectMnemonic is returned if we detect that the checksum of
|
||||
// the specified mnemonic doesn't match. This indicates the user input
|
||||
// the wrong mnemonic.
|
||||
ErrIncorrectMnemonic = fmt.Errorf("mnemonic phrase checksum doesn't" +
|
||||
"match")
|
||||
)
|
||||
|
||||
// ErrUnknownMnenomicWord is returned when attempting to decipher and
|
||||
// enciphered mnemonic, but a word encountered isn't a member of our word list.
|
||||
type ErrUnknownMnenomicWord struct {
|
||||
word string
|
||||
}
|
||||
|
||||
// Error returns a human readable string describing the error.
|
||||
func (e ErrUnknownMnenomicWord) Error() string {
|
||||
return fmt.Sprintf("word %v isn't a part of default word list", e.word)
|
||||
}
|
2073
aezeed/wordlist.go
Normal file
2073
aezeed/wordlist.go
Normal file
@ -0,0 +1,2073 @@
|
||||
package aezeed
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// reverseWordMap maps a word to its position within the default word list.
|
||||
reverseWordMap map[string]int
|
||||
)
|
||||
|
||||
func init() {
|
||||
reverseWordMap = make(map[string]int)
|
||||
for i, v := range defaultWordList {
|
||||
reverseWordMap[v] = i
|
||||
}
|
||||
}
|
||||
|
||||
// defaultWordList is a slice of the current default word list that's used to
|
||||
// encode the enciphered seed into a human readable set of words.
|
||||
var defaultWordList = strings.Split(englishWordList, "\n")
|
||||
|
||||
// englishWordList is an English wordlist that's used as part of version 0 of
|
||||
// the cipherseed scheme. This is the *same* word list that's recommend for use
|
||||
// with BIP0039.
|
||||
var englishWordList = `abandon
|
||||
ability
|
||||
able
|
||||
about
|
||||
above
|
||||
absent
|
||||
absorb
|
||||
abstract
|
||||
absurd
|
||||
abuse
|
||||
access
|
||||
accident
|
||||
account
|
||||
accuse
|
||||
achieve
|
||||
acid
|
||||
acoustic
|
||||
acquire
|
||||
across
|
||||
act
|
||||
action
|
||||
actor
|
||||
actress
|
||||
actual
|
||||
adapt
|
||||
add
|
||||
addict
|
||||
address
|
||||
adjust
|
||||
admit
|
||||
adult
|
||||
advance
|
||||
advice
|
||||
aerobic
|
||||
affair
|
||||
afford
|
||||
afraid
|
||||
again
|
||||
age
|
||||
agent
|
||||
agree
|
||||
ahead
|
||||
aim
|
||||
air
|
||||
airport
|
||||
aisle
|
||||
alarm
|
||||
album
|
||||
alcohol
|
||||
alert
|
||||
alien
|
||||
all
|
||||
alley
|
||||
allow
|
||||
almost
|
||||
alone
|
||||
alpha
|
||||
already
|
||||
also
|
||||
alter
|
||||
always
|
||||
amateur
|
||||
amazing
|
||||
among
|
||||
amount
|
||||
amused
|
||||
analyst
|
||||
anchor
|
||||
ancient
|
||||
anger
|
||||
angle
|
||||
angry
|
||||
animal
|
||||
ankle
|
||||
announce
|
||||
annual
|
||||
another
|
||||
answer
|
||||
antenna
|
||||
antique
|
||||
anxiety
|
||||
any
|
||||
apart
|
||||
apology
|
||||
appear
|
||||
apple
|
||||
approve
|
||||
april
|
||||
arch
|
||||
arctic
|
||||
area
|
||||
arena
|
||||
argue
|
||||
arm
|
||||
armed
|
||||
armor
|
||||
army
|
||||
around
|
||||
arrange
|
||||
arrest
|
||||
arrive
|
||||
arrow
|
||||
art
|
||||
artefact
|
||||
artist
|
||||
artwork
|
||||
ask
|
||||
aspect
|
||||
assault
|
||||
asset
|
||||
assist
|
||||
assume
|
||||
asthma
|
||||
athlete
|
||||
atom
|
||||
attack
|
||||
attend
|
||||
attitude
|
||||
attract
|
||||
auction
|
||||
audit
|
||||
august
|
||||
aunt
|
||||
author
|
||||
auto
|
||||
autumn
|
||||
average
|
||||
avocado
|
||||
avoid
|
||||
awake
|
||||
aware
|
||||
away
|
||||
awesome
|
||||
awful
|
||||
awkward
|
||||
axis
|
||||
baby
|
||||
bachelor
|
||||
bacon
|
||||
badge
|
||||
bag
|
||||
balance
|
||||
balcony
|
||||
ball
|
||||
bamboo
|
||||
banana
|
||||
banner
|
||||
bar
|
||||
barely
|
||||
bargain
|
||||
barrel
|
||||
base
|
||||
basic
|
||||
basket
|
||||
battle
|
||||
beach
|
||||
bean
|
||||
beauty
|
||||
because
|
||||
become
|
||||
beef
|
||||
before
|
||||
begin
|
||||
behave
|
||||
behind
|
||||
believe
|
||||
below
|
||||
belt
|
||||
bench
|
||||
benefit
|
||||
best
|
||||
betray
|
||||
better
|
||||
between
|
||||
beyond
|
||||
bicycle
|
||||
bid
|
||||
bike
|
||||
bind
|
||||
biology
|
||||
bird
|
||||
birth
|
||||
bitter
|
||||
black
|
||||
blade
|
||||
blame
|
||||
blanket
|
||||
blast
|
||||
bleak
|
||||
bless
|
||||
blind
|
||||
blood
|
||||
blossom
|
||||
blouse
|
||||
blue
|
||||
blur
|
||||
blush
|
||||
board
|
||||
boat
|
||||
body
|
||||
boil
|
||||
bomb
|
||||
bone
|
||||
bonus
|
||||
book
|
||||
boost
|
||||
border
|
||||
boring
|
||||
borrow
|
||||
boss
|
||||
bottom
|
||||
bounce
|
||||
box
|
||||
boy
|
||||
bracket
|
||||
brain
|
||||
brand
|
||||
brass
|
||||
brave
|
||||
bread
|
||||
breeze
|
||||
brick
|
||||
bridge
|
||||
brief
|
||||
bright
|
||||
bring
|
||||
brisk
|
||||
broccoli
|
||||
broken
|
||||
bronze
|
||||
broom
|
||||
brother
|
||||
brown
|
||||
brush
|
||||
bubble
|
||||
buddy
|
||||
budget
|
||||
buffalo
|
||||
build
|
||||
bulb
|
||||
bulk
|
||||
bullet
|
||||
bundle
|
||||
bunker
|
||||
burden
|
||||
burger
|
||||
burst
|
||||
bus
|
||||
business
|
||||
busy
|
||||
butter
|
||||
buyer
|
||||
buzz
|
||||
cabbage
|
||||
cabin
|
||||
cable
|
||||
cactus
|
||||
cage
|
||||
cake
|
||||
call
|
||||
calm
|
||||
camera
|
||||
camp
|
||||
can
|
||||
canal
|
||||
cancel
|
||||
candy
|
||||
cannon
|
||||
canoe
|
||||
canvas
|
||||
canyon
|
||||
capable
|
||||
capital
|
||||
captain
|
||||
car
|
||||
carbon
|
||||
card
|
||||
cargo
|
||||
carpet
|
||||
carry
|
||||
cart
|
||||
case
|
||||
cash
|
||||
casino
|
||||
castle
|
||||
casual
|
||||
cat
|
||||
catalog
|
||||
catch
|
||||
category
|
||||
cattle
|
||||
caught
|
||||
cause
|
||||
caution
|
||||
cave
|
||||
ceiling
|
||||
celery
|
||||
cement
|
||||
census
|
||||
century
|
||||
cereal
|
||||
certain
|
||||
chair
|
||||
chalk
|
||||
champion
|
||||
change
|
||||
chaos
|
||||
chapter
|
||||
charge
|
||||
chase
|
||||
chat
|
||||
cheap
|
||||
check
|
||||
cheese
|
||||
chef
|
||||
cherry
|
||||
chest
|
||||
chicken
|
||||
chief
|
||||
child
|
||||
chimney
|
||||
choice
|
||||
choose
|
||||
chronic
|
||||
chuckle
|
||||
chunk
|
||||
churn
|
||||
cigar
|
||||
cinnamon
|
||||
circle
|
||||
citizen
|
||||
city
|
||||
civil
|
||||
claim
|
||||
clap
|
||||
clarify
|
||||
claw
|
||||
clay
|
||||
clean
|
||||
clerk
|
||||
clever
|
||||
click
|
||||
client
|
||||
cliff
|
||||
climb
|
||||
clinic
|
||||
clip
|
||||
clock
|
||||
clog
|
||||
close
|
||||
cloth
|
||||
cloud
|
||||
clown
|
||||
club
|
||||
clump
|
||||
cluster
|
||||
clutch
|
||||
coach
|
||||
coast
|
||||
coconut
|
||||
code
|
||||
coffee
|
||||
coil
|
||||
coin
|
||||
collect
|
||||
color
|
||||
column
|
||||
combine
|
||||
come
|
||||
comfort
|
||||
comic
|
||||
common
|
||||
company
|
||||
concert
|
||||
conduct
|
||||
confirm
|
||||
congress
|
||||
connect
|
||||
consider
|
||||
control
|
||||
convince
|
||||
cook
|
||||
cool
|
||||
copper
|
||||
copy
|
||||
coral
|
||||
core
|
||||
corn
|
||||
correct
|
||||
cost
|
||||
cotton
|
||||
couch
|
||||
country
|
||||
couple
|
||||
course
|
||||
cousin
|
||||
cover
|
||||
coyote
|
||||
crack
|
||||
cradle
|
||||
craft
|
||||
cram
|
||||
crane
|
||||
crash
|
||||
crater
|
||||
crawl
|
||||
crazy
|
||||
cream
|
||||
credit
|
||||
creek
|
||||
crew
|
||||
cricket
|
||||
crime
|
||||
crisp
|
||||
critic
|
||||
crop
|
||||
cross
|
||||
crouch
|
||||
crowd
|
||||
crucial
|
||||
cruel
|
||||
cruise
|
||||
crumble
|
||||
crunch
|
||||
crush
|
||||
cry
|
||||
crystal
|
||||
cube
|
||||
culture
|
||||
cup
|
||||
cupboard
|
||||
curious
|
||||
current
|
||||
curtain
|
||||
curve
|
||||
cushion
|
||||
custom
|
||||
cute
|
||||
cycle
|
||||
dad
|
||||
damage
|
||||
damp
|
||||
dance
|
||||
danger
|
||||
daring
|
||||
dash
|
||||
daughter
|
||||
dawn
|
||||
day
|
||||
deal
|
||||
debate
|
||||
debris
|
||||
decade
|
||||
december
|
||||
decide
|
||||
decline
|
||||
decorate
|
||||
decrease
|
||||
deer
|
||||
defense
|
||||
define
|
||||
defy
|
||||
degree
|
||||
delay
|
||||
deliver
|
||||
demand
|
||||
demise
|
||||
denial
|
||||
dentist
|
||||
deny
|
||||
depart
|
||||
depend
|
||||
deposit
|
||||
depth
|
||||
deputy
|
||||
derive
|
||||
describe
|
||||
desert
|
||||
design
|
||||
desk
|
||||
despair
|
||||
destroy
|
||||
detail
|
||||
detect
|
||||
develop
|
||||
device
|
||||
devote
|
||||
diagram
|
||||
dial
|
||||
diamond
|
||||
diary
|
||||
dice
|
||||
diesel
|
||||
diet
|
||||
differ
|
||||
digital
|
||||
dignity
|
||||
dilemma
|
||||
dinner
|
||||
dinosaur
|
||||
direct
|
||||
dirt
|
||||
disagree
|
||||
discover
|
||||
disease
|
||||
dish
|
||||
dismiss
|
||||
disorder
|
||||
display
|
||||
distance
|
||||
divert
|
||||
divide
|
||||
divorce
|
||||
dizzy
|
||||
doctor
|
||||
document
|
||||
dog
|
||||
doll
|
||||
dolphin
|
||||
domain
|
||||
donate
|
||||
donkey
|
||||
donor
|
||||
door
|
||||
dose
|
||||
double
|
||||
dove
|
||||
draft
|
||||
dragon
|
||||
drama
|
||||
drastic
|
||||
draw
|
||||
dream
|
||||
dress
|
||||
drift
|
||||
drill
|
||||
drink
|
||||
drip
|
||||
drive
|
||||
drop
|
||||
drum
|
||||
dry
|
||||
duck
|
||||
dumb
|
||||
dune
|
||||
during
|
||||
dust
|
||||
dutch
|
||||
duty
|
||||
dwarf
|
||||
dynamic
|
||||
eager
|
||||
eagle
|
||||
early
|
||||
earn
|
||||
earth
|
||||
easily
|
||||
east
|
||||
easy
|
||||
echo
|
||||
ecology
|
||||
economy
|
||||
edge
|
||||
edit
|
||||
educate
|
||||
effort
|
||||
egg
|
||||
eight
|
||||
either
|
||||
elbow
|
||||
elder
|
||||
electric
|
||||
elegant
|
||||
element
|
||||
elephant
|
||||
elevator
|
||||
elite
|
||||
else
|
||||
embark
|
||||
embody
|
||||
embrace
|
||||
emerge
|
||||
emotion
|
||||
employ
|
||||
empower
|
||||
empty
|
||||
enable
|
||||
enact
|
||||
end
|
||||
endless
|
||||
endorse
|
||||
enemy
|
||||
energy
|
||||
enforce
|
||||
engage
|
||||
engine
|
||||
enhance
|
||||
enjoy
|
||||
enlist
|
||||
enough
|
||||
enrich
|
||||
enroll
|
||||
ensure
|
||||
enter
|
||||
entire
|
||||
entry
|
||||
envelope
|
||||
episode
|
||||
equal
|
||||
equip
|
||||
era
|
||||
erase
|
||||
erode
|
||||
erosion
|
||||
error
|
||||
erupt
|
||||
escape
|
||||
essay
|
||||
essence
|
||||
estate
|
||||
eternal
|
||||
ethics
|
||||
evidence
|
||||
evil
|
||||
evoke
|
||||
evolve
|
||||
exact
|
||||
example
|
||||
excess
|
||||
exchange
|
||||
excite
|
||||
exclude
|
||||
excuse
|
||||
execute
|
||||
exercise
|
||||
exhaust
|
||||
exhibit
|
||||
exile
|
||||
exist
|
||||
exit
|
||||
exotic
|
||||
expand
|
||||
expect
|
||||
expire
|
||||
explain
|
||||
expose
|
||||
express
|
||||
extend
|
||||
extra
|
||||
eye
|
||||
eyebrow
|
||||
fabric
|
||||
face
|
||||
faculty
|
||||
fade
|
||||
faint
|
||||
faith
|
||||
fall
|
||||
false
|
||||
fame
|
||||
family
|
||||
famous
|
||||
fan
|
||||
fancy
|
||||
fantasy
|
||||
farm
|
||||
fashion
|
||||
fat
|
||||
fatal
|
||||
father
|
||||
fatigue
|
||||
fault
|
||||
favorite
|
||||
feature
|
||||
february
|
||||
federal
|
||||
fee
|
||||
feed
|
||||
feel
|
||||
female
|
||||
fence
|
||||
festival
|
||||
fetch
|
||||
fever
|
||||
few
|
||||
fiber
|
||||
fiction
|
||||
field
|
||||
figure
|
||||
file
|
||||
film
|
||||
filter
|
||||
final
|
||||
find
|
||||
fine
|
||||
finger
|
||||
finish
|
||||
fire
|
||||
firm
|
||||
first
|
||||
fiscal
|
||||
fish
|
||||
fit
|
||||
fitness
|
||||
fix
|
||||
flag
|
||||
flame
|
||||
flash
|
||||
flat
|
||||
flavor
|
||||
flee
|
||||
flight
|
||||
flip
|
||||
float
|
||||
flock
|
||||
floor
|
||||
flower
|
||||
fluid
|
||||
flush
|
||||
fly
|
||||
foam
|
||||
focus
|
||||
fog
|
||||
foil
|
||||
fold
|
||||
follow
|
||||
food
|
||||
foot
|
||||
force
|
||||
forest
|
||||
forget
|
||||
fork
|
||||
fortune
|
||||
forum
|
||||
forward
|
||||
fossil
|
||||
foster
|
||||
found
|
||||
fox
|
||||
fragile
|
||||
frame
|
||||
frequent
|
||||
fresh
|
||||
friend
|
||||
fringe
|
||||
frog
|
||||
front
|
||||
frost
|
||||
frown
|
||||
frozen
|
||||
fruit
|
||||
fuel
|
||||
fun
|
||||
funny
|
||||
furnace
|
||||
fury
|
||||
future
|
||||
gadget
|
||||
gain
|
||||
galaxy
|
||||
gallery
|
||||
game
|
||||
gap
|
||||
garage
|
||||
garbage
|
||||
garden
|
||||
garlic
|
||||
garment
|
||||
gas
|
||||
gasp
|
||||
gate
|
||||
gather
|
||||
gauge
|
||||
gaze
|
||||
general
|
||||
genius
|
||||
genre
|
||||
gentle
|
||||
genuine
|
||||
gesture
|
||||
ghost
|
||||
giant
|
||||
gift
|
||||
giggle
|
||||
ginger
|
||||
giraffe
|
||||
girl
|
||||
give
|
||||
glad
|
||||
glance
|
||||
glare
|
||||
glass
|
||||
glide
|
||||
glimpse
|
||||
globe
|
||||
gloom
|
||||
glory
|
||||
glove
|
||||
glow
|
||||
glue
|
||||
goat
|
||||
goddess
|
||||
gold
|
||||
good
|
||||
goose
|
||||
gorilla
|
||||
gospel
|
||||
gossip
|
||||
govern
|
||||
gown
|
||||
grab
|
||||
grace
|
||||
grain
|
||||
grant
|
||||
grape
|
||||
grass
|
||||
gravity
|
||||
great
|
||||
green
|
||||
grid
|
||||
grief
|
||||
grit
|
||||
grocery
|
||||
group
|
||||
grow
|
||||
grunt
|
||||
guard
|
||||
guess
|
||||
guide
|
||||
guilt
|
||||
guitar
|
||||
gun
|
||||
gym
|
||||
habit
|
||||
hair
|
||||
half
|
||||
hammer
|
||||
hamster
|
||||
hand
|
||||
happy
|
||||
harbor
|
||||
hard
|
||||
harsh
|
||||
harvest
|
||||
hat
|
||||
have
|
||||
hawk
|
||||
hazard
|
||||
head
|
||||
health
|
||||
heart
|
||||
heavy
|
||||
hedgehog
|
||||
height
|
||||
hello
|
||||
helmet
|
||||
help
|
||||
hen
|
||||
hero
|
||||
hidden
|
||||
high
|
||||
hill
|
||||
hint
|
||||
hip
|
||||
hire
|
||||
history
|
||||
hobby
|
||||
hockey
|
||||
hold
|
||||
hole
|
||||
holiday
|
||||
hollow
|
||||
home
|
||||
honey
|
||||
hood
|
||||
hope
|
||||
horn
|
||||
horror
|
||||
horse
|
||||
hospital
|
||||
host
|
||||
hotel
|
||||
hour
|
||||
hover
|
||||
hub
|
||||
huge
|
||||
human
|
||||
humble
|
||||
humor
|
||||
hundred
|
||||
hungry
|
||||
hunt
|
||||
hurdle
|
||||
hurry
|
||||
hurt
|
||||
husband
|
||||
hybrid
|
||||
ice
|
||||
icon
|
||||
idea
|
||||
identify
|
||||
idle
|
||||
ignore
|
||||
ill
|
||||
illegal
|
||||
illness
|
||||
image
|
||||
imitate
|
||||
immense
|
||||
immune
|
||||
impact
|
||||
impose
|
||||
improve
|
||||
impulse
|
||||
inch
|
||||
include
|
||||
income
|
||||
increase
|
||||
index
|
||||
indicate
|
||||
indoor
|
||||
industry
|
||||
infant
|
||||
inflict
|
||||
inform
|
||||
inhale
|
||||
inherit
|
||||
initial
|
||||
inject
|
||||
injury
|
||||
inmate
|
||||
inner
|
||||
innocent
|
||||
input
|
||||
inquiry
|
||||
insane
|
||||
insect
|
||||
inside
|
||||
inspire
|
||||
install
|
||||
intact
|
||||
interest
|
||||
into
|
||||
invest
|
||||
invite
|
||||
involve
|
||||
iron
|
||||
island
|
||||
isolate
|
||||
issue
|
||||
item
|
||||
ivory
|
||||
jacket
|
||||
jaguar
|
||||
jar
|
||||
jazz
|
||||
jealous
|
||||
jeans
|
||||
jelly
|
||||
jewel
|
||||
job
|
||||
join
|
||||
joke
|
||||
journey
|
||||
joy
|
||||
judge
|
||||
juice
|
||||
jump
|
||||
jungle
|
||||
junior
|
||||
junk
|
||||
just
|
||||
kangaroo
|
||||
keen
|
||||
keep
|
||||
ketchup
|
||||
key
|
||||
kick
|
||||
kid
|
||||
kidney
|
||||
kind
|
||||
kingdom
|
||||
kiss
|
||||
kit
|
||||
kitchen
|
||||
kite
|
||||
kitten
|
||||
kiwi
|
||||
knee
|
||||
knife
|
||||
knock
|
||||
know
|
||||
lab
|
||||
label
|
||||
labor
|
||||
ladder
|
||||
lady
|
||||
lake
|
||||
lamp
|
||||
language
|
||||
laptop
|
||||
large
|
||||
later
|
||||
latin
|
||||
laugh
|
||||
laundry
|
||||
lava
|
||||
law
|
||||
lawn
|
||||
lawsuit
|
||||
layer
|
||||
lazy
|
||||
leader
|
||||
leaf
|
||||
learn
|
||||
leave
|
||||
lecture
|
||||
left
|
||||
leg
|
||||
legal
|
||||
legend
|
||||
leisure
|
||||
lemon
|
||||
lend
|
||||
length
|
||||
lens
|
||||
leopard
|
||||
lesson
|
||||
letter
|
||||
level
|
||||
liar
|
||||
liberty
|
||||
library
|
||||
license
|
||||
life
|
||||
lift
|
||||
light
|
||||
like
|
||||
limb
|
||||
limit
|
||||
link
|
||||
lion
|
||||
liquid
|
||||
list
|
||||
little
|
||||
live
|
||||
lizard
|
||||
load
|
||||
loan
|
||||
lobster
|
||||
local
|
||||
lock
|
||||
logic
|
||||
lonely
|
||||
long
|
||||
loop
|
||||
lottery
|
||||
loud
|
||||
lounge
|
||||
love
|
||||
loyal
|
||||
lucky
|
||||
luggage
|
||||
lumber
|
||||
lunar
|
||||
lunch
|
||||
luxury
|
||||
lyrics
|
||||
machine
|
||||
mad
|
||||
magic
|
||||
magnet
|
||||
maid
|
||||
mail
|
||||
main
|
||||
major
|
||||
make
|
||||
mammal
|
||||
man
|
||||
manage
|
||||
mandate
|
||||
mango
|
||||
mansion
|
||||
manual
|
||||
maple
|
||||
marble
|
||||
march
|
||||
margin
|
||||
marine
|
||||
market
|
||||
marriage
|
||||
mask
|
||||
mass
|
||||
master
|
||||
match
|
||||
material
|
||||
math
|
||||
matrix
|
||||
matter
|
||||
maximum
|
||||
maze
|
||||
meadow
|
||||
mean
|
||||
measure
|
||||
meat
|
||||
mechanic
|
||||
medal
|
||||
media
|
||||
melody
|
||||
melt
|
||||
member
|
||||
memory
|
||||
mention
|
||||
menu
|
||||
mercy
|
||||
merge
|
||||
merit
|
||||
merry
|
||||
mesh
|
||||
message
|
||||
metal
|
||||
method
|
||||
middle
|
||||
midnight
|
||||
milk
|
||||
million
|
||||
mimic
|
||||
mind
|
||||
minimum
|
||||
minor
|
||||
minute
|
||||
miracle
|
||||
mirror
|
||||
misery
|
||||
miss
|
||||
mistake
|
||||
mix
|
||||
mixed
|
||||
mixture
|
||||
mobile
|
||||
model
|
||||
modify
|
||||
mom
|
||||
moment
|
||||
monitor
|
||||
monkey
|
||||
monster
|
||||
month
|
||||
moon
|
||||
moral
|
||||
more
|
||||
morning
|
||||
mosquito
|
||||
mother
|
||||
motion
|
||||
motor
|
||||
mountain
|
||||
mouse
|
||||
move
|
||||
movie
|
||||
much
|
||||
muffin
|
||||
mule
|
||||
multiply
|
||||
muscle
|
||||
museum
|
||||
mushroom
|
||||
music
|
||||
must
|
||||
mutual
|
||||
myself
|
||||
mystery
|
||||
myth
|
||||
naive
|
||||
name
|
||||
napkin
|
||||
narrow
|
||||
nasty
|
||||
nation
|
||||
nature
|
||||
near
|
||||
neck
|
||||
need
|
||||
negative
|
||||
neglect
|
||||
neither
|
||||
nephew
|
||||
nerve
|
||||
nest
|
||||
net
|
||||
network
|
||||
neutral
|
||||
never
|
||||
news
|
||||
next
|
||||
nice
|
||||
night
|
||||
noble
|
||||
noise
|
||||
nominee
|
||||
noodle
|
||||
normal
|
||||
north
|
||||
nose
|
||||
notable
|
||||
note
|
||||
nothing
|
||||
notice
|
||||
novel
|
||||
now
|
||||
nuclear
|
||||
number
|
||||
nurse
|
||||
nut
|
||||
oak
|
||||
obey
|
||||
object
|
||||
oblige
|
||||
obscure
|
||||
observe
|
||||
obtain
|
||||
obvious
|
||||
occur
|
||||
ocean
|
||||
october
|
||||
odor
|
||||
off
|
||||
offer
|
||||
office
|
||||
often
|
||||
oil
|
||||
okay
|
||||
old
|
||||
olive
|
||||
olympic
|
||||
omit
|
||||
once
|
||||
one
|
||||
onion
|
||||
online
|
||||
only
|
||||
open
|
||||
opera
|
||||
opinion
|
||||
oppose
|
||||
option
|
||||
orange
|
||||
orbit
|
||||
orchard
|
||||
order
|
||||
ordinary
|
||||
organ
|
||||
orient
|
||||
original
|
||||
orphan
|
||||
ostrich
|
||||
other
|
||||
outdoor
|
||||
outer
|
||||
output
|
||||
outside
|
||||
oval
|
||||
oven
|
||||
over
|
||||
own
|
||||
owner
|
||||
oxygen
|
||||
oyster
|
||||
ozone
|
||||
pact
|
||||
paddle
|
||||
page
|
||||
pair
|
||||
palace
|
||||
palm
|
||||
panda
|
||||
panel
|
||||
panic
|
||||
panther
|
||||
paper
|
||||
parade
|
||||
parent
|
||||
park
|
||||
parrot
|
||||
party
|
||||
pass
|
||||
patch
|
||||
path
|
||||
patient
|
||||
patrol
|
||||
pattern
|
||||
pause
|
||||
pave
|
||||
payment
|
||||
peace
|
||||
peanut
|
||||
pear
|
||||
peasant
|
||||
pelican
|
||||
pen
|
||||
penalty
|
||||
pencil
|
||||
people
|
||||
pepper
|
||||
perfect
|
||||
permit
|
||||
person
|
||||
pet
|
||||
phone
|
||||
photo
|
||||
phrase
|
||||
physical
|
||||
piano
|
||||
picnic
|
||||
picture
|
||||
piece
|
||||
pig
|
||||
pigeon
|
||||
pill
|
||||
pilot
|
||||
pink
|
||||
pioneer
|
||||
pipe
|
||||
pistol
|
||||
pitch
|
||||
pizza
|
||||
place
|
||||
planet
|
||||
plastic
|
||||
plate
|
||||
play
|
||||
please
|
||||
pledge
|
||||
pluck
|
||||
plug
|
||||
plunge
|
||||
poem
|
||||
poet
|
||||
point
|
||||
polar
|
||||
pole
|
||||
police
|
||||
pond
|
||||
pony
|
||||
pool
|
||||
popular
|
||||
portion
|
||||
position
|
||||
possible
|
||||
post
|
||||
potato
|
||||
pottery
|
||||
poverty
|
||||
powder
|
||||
power
|
||||
practice
|
||||
praise
|
||||
predict
|
||||
prefer
|
||||
prepare
|
||||
present
|
||||
pretty
|
||||
prevent
|
||||
price
|
||||
pride
|
||||
primary
|
||||
print
|
||||
priority
|
||||
prison
|
||||
private
|
||||
prize
|
||||
problem
|
||||
process
|
||||
produce
|
||||
profit
|
||||
program
|
||||
project
|
||||
promote
|
||||
proof
|
||||
property
|
||||
prosper
|
||||
protect
|
||||
proud
|
||||
provide
|
||||
public
|
||||
pudding
|
||||
pull
|
||||
pulp
|
||||
pulse
|
||||
pumpkin
|
||||
punch
|
||||
pupil
|
||||
puppy
|
||||
purchase
|
||||
purity
|
||||
purpose
|
||||
purse
|
||||
push
|
||||
put
|
||||
puzzle
|
||||
pyramid
|
||||
quality
|
||||
quantum
|
||||
quarter
|
||||
question
|
||||
quick
|
||||
quit
|
||||
quiz
|
||||
quote
|
||||
rabbit
|
||||
raccoon
|
||||
race
|
||||
rack
|
||||
radar
|
||||
radio
|
||||
rail
|
||||
rain
|
||||
raise
|
||||
rally
|
||||
ramp
|
||||
ranch
|
||||
random
|
||||
range
|
||||
rapid
|
||||
rare
|
||||
rate
|
||||
rather
|
||||
raven
|
||||
raw
|
||||
razor
|
||||
ready
|
||||
real
|
||||
reason
|
||||
rebel
|
||||
rebuild
|
||||
recall
|
||||
receive
|
||||
recipe
|
||||
record
|
||||
recycle
|
||||
reduce
|
||||
reflect
|
||||
reform
|
||||
refuse
|
||||
region
|
||||
regret
|
||||
regular
|
||||
reject
|
||||
relax
|
||||
release
|
||||
relief
|
||||
rely
|
||||
remain
|
||||
remember
|
||||
remind
|
||||
remove
|
||||
render
|
||||
renew
|
||||
rent
|
||||
reopen
|
||||
repair
|
||||
repeat
|
||||
replace
|
||||
report
|
||||
require
|
||||
rescue
|
||||
resemble
|
||||
resist
|
||||
resource
|
||||
response
|
||||
result
|
||||
retire
|
||||
retreat
|
||||
return
|
||||
reunion
|
||||
reveal
|
||||
review
|
||||
reward
|
||||
rhythm
|
||||
rib
|
||||
ribbon
|
||||
rice
|
||||
rich
|
||||
ride
|
||||
ridge
|
||||
rifle
|
||||
right
|
||||
rigid
|
||||
ring
|
||||
riot
|
||||
ripple
|
||||
risk
|
||||
ritual
|
||||
rival
|
||||
river
|
||||
road
|
||||
roast
|
||||
robot
|
||||
robust
|
||||
rocket
|
||||
romance
|
||||
roof
|
||||
rookie
|
||||
room
|
||||
rose
|
||||
rotate
|
||||
rough
|
||||
round
|
||||
route
|
||||
royal
|
||||
rubber
|
||||
rude
|
||||
rug
|
||||
rule
|
||||
run
|
||||
runway
|
||||
rural
|
||||
sad
|
||||
saddle
|
||||
sadness
|
||||
safe
|
||||
sail
|
||||
salad
|
||||
salmon
|
||||
salon
|
||||
salt
|
||||
salute
|
||||
same
|
||||
sample
|
||||
sand
|
||||
satisfy
|
||||
satoshi
|
||||
sauce
|
||||
sausage
|
||||
save
|
||||
say
|
||||
scale
|
||||
scan
|
||||
scare
|
||||
scatter
|
||||
scene
|
||||
scheme
|
||||
school
|
||||
science
|
||||
scissors
|
||||
scorpion
|
||||
scout
|
||||
scrap
|
||||
screen
|
||||
script
|
||||
scrub
|
||||
sea
|
||||
search
|
||||
season
|
||||
seat
|
||||
second
|
||||
secret
|
||||
section
|
||||
security
|
||||
seed
|
||||
seek
|
||||
segment
|
||||
select
|
||||
sell
|
||||
seminar
|
||||
senior
|
||||
sense
|
||||
sentence
|
||||
series
|
||||
service
|
||||
session
|
||||
settle
|
||||
setup
|
||||
seven
|
||||
shadow
|
||||
shaft
|
||||
shallow
|
||||
share
|
||||
shed
|
||||
shell
|
||||
sheriff
|
||||
shield
|
||||
shift
|
||||
shine
|
||||
ship
|
||||
shiver
|
||||
shock
|
||||
shoe
|
||||
shoot
|
||||
shop
|
||||
short
|
||||
shoulder
|
||||
shove
|
||||
shrimp
|
||||
shrug
|
||||
shuffle
|
||||
shy
|
||||
sibling
|
||||
sick
|
||||
side
|
||||
siege
|
||||
sight
|
||||
sign
|
||||
silent
|
||||
silk
|
||||
silly
|
||||
silver
|
||||
similar
|
||||
simple
|
||||
since
|
||||
sing
|
||||
siren
|
||||
sister
|
||||
situate
|
||||
six
|
||||
size
|
||||
skate
|
||||
sketch
|
||||
ski
|
||||
skill
|
||||
skin
|
||||
skirt
|
||||
skull
|
||||
slab
|
||||
slam
|
||||
sleep
|
||||
slender
|
||||
slice
|
||||
slide
|
||||
slight
|
||||
slim
|
||||
slogan
|
||||
slot
|
||||
slow
|
||||
slush
|
||||
small
|
||||
smart
|
||||
smile
|
||||
smoke
|
||||
smooth
|
||||
snack
|
||||
snake
|
||||
snap
|
||||
sniff
|
||||
snow
|
||||
soap
|
||||
soccer
|
||||
social
|
||||
sock
|
||||
soda
|
||||
soft
|
||||
solar
|
||||
soldier
|
||||
solid
|
||||
solution
|
||||
solve
|
||||
someone
|
||||
song
|
||||
soon
|
||||
sorry
|
||||
sort
|
||||
soul
|
||||
sound
|
||||
soup
|
||||
source
|
||||
south
|
||||
space
|
||||
spare
|
||||
spatial
|
||||
spawn
|
||||
speak
|
||||
special
|
||||
speed
|
||||
spell
|
||||
spend
|
||||
sphere
|
||||
spice
|
||||
spider
|
||||
spike
|
||||
spin
|
||||
spirit
|
||||
split
|
||||
spoil
|
||||
sponsor
|
||||
spoon
|
||||
sport
|
||||
spot
|
||||
spray
|
||||
spread
|
||||
spring
|
||||
spy
|
||||
square
|
||||
squeeze
|
||||
squirrel
|
||||
stable
|
||||
stadium
|
||||
staff
|
||||
stage
|
||||
stairs
|
||||
stamp
|
||||
stand
|
||||
start
|
||||
state
|
||||
stay
|
||||
steak
|
||||
steel
|
||||
stem
|
||||
step
|
||||
stereo
|
||||
stick
|
||||
still
|
||||
sting
|
||||
stock
|
||||
stomach
|
||||
stone
|
||||
stool
|
||||
story
|
||||
stove
|
||||
strategy
|
||||
street
|
||||
strike
|
||||
strong
|
||||
struggle
|
||||
student
|
||||
stuff
|
||||
stumble
|
||||
style
|
||||
subject
|
||||
submit
|
||||
subway
|
||||
success
|
||||
such
|
||||
sudden
|
||||
suffer
|
||||
sugar
|
||||
suggest
|
||||
suit
|
||||
summer
|
||||
sun
|
||||
sunny
|
||||
sunset
|
||||
super
|
||||
supply
|
||||
supreme
|
||||
sure
|
||||
surface
|
||||
surge
|
||||
surprise
|
||||
surround
|
||||
survey
|
||||
suspect
|
||||
sustain
|
||||
swallow
|
||||
swamp
|
||||
swap
|
||||
swarm
|
||||
swear
|
||||
sweet
|
||||
swift
|
||||
swim
|
||||
swing
|
||||
switch
|
||||
sword
|
||||
symbol
|
||||
symptom
|
||||
syrup
|
||||
system
|
||||
table
|
||||
tackle
|
||||
tag
|
||||
tail
|
||||
talent
|
||||
talk
|
||||
tank
|
||||
tape
|
||||
target
|
||||
task
|
||||
taste
|
||||
tattoo
|
||||
taxi
|
||||
teach
|
||||
team
|
||||
tell
|
||||
ten
|
||||
tenant
|
||||
tennis
|
||||
tent
|
||||
term
|
||||
test
|
||||
text
|
||||
thank
|
||||
that
|
||||
theme
|
||||
then
|
||||
theory
|
||||
there
|
||||
they
|
||||
thing
|
||||
this
|
||||
thought
|
||||
three
|
||||
thrive
|
||||
throw
|
||||
thumb
|
||||
thunder
|
||||
ticket
|
||||
tide
|
||||
tiger
|
||||
tilt
|
||||
timber
|
||||
time
|
||||
tiny
|
||||
tip
|
||||
tired
|
||||
tissue
|
||||
title
|
||||
toast
|
||||
tobacco
|
||||
today
|
||||
toddler
|
||||
toe
|
||||
together
|
||||
toilet
|
||||
token
|
||||
tomato
|
||||
tomorrow
|
||||
tone
|
||||
tongue
|
||||
tonight
|
||||
tool
|
||||
tooth
|
||||
top
|
||||
topic
|
||||
topple
|
||||
torch
|
||||
tornado
|
||||
tortoise
|
||||
toss
|
||||
total
|
||||
tourist
|
||||
toward
|
||||
tower
|
||||
town
|
||||
toy
|
||||
track
|
||||
trade
|
||||
traffic
|
||||
tragic
|
||||
train
|
||||
transfer
|
||||
trap
|
||||
trash
|
||||
travel
|
||||
tray
|
||||
treat
|
||||
tree
|
||||
trend
|
||||
trial
|
||||
tribe
|
||||
trick
|
||||
trigger
|
||||
trim
|
||||
trip
|
||||
trophy
|
||||
trouble
|
||||
truck
|
||||
true
|
||||
truly
|
||||
trumpet
|
||||
trust
|
||||
truth
|
||||
try
|
||||
tube
|
||||
tuition
|
||||
tumble
|
||||
tuna
|
||||
tunnel
|
||||
turkey
|
||||
turn
|
||||
turtle
|
||||
twelve
|
||||
twenty
|
||||
twice
|
||||
twin
|
||||
twist
|
||||
two
|
||||
type
|
||||
typical
|
||||
ugly
|
||||
umbrella
|
||||
unable
|
||||
unaware
|
||||
uncle
|
||||
uncover
|
||||
under
|
||||
undo
|
||||
unfair
|
||||
unfold
|
||||
unhappy
|
||||
uniform
|
||||
unique
|
||||
unit
|
||||
universe
|
||||
unknown
|
||||
unlock
|
||||
until
|
||||
unusual
|
||||
unveil
|
||||
update
|
||||
upgrade
|
||||
uphold
|
||||
upon
|
||||
upper
|
||||
upset
|
||||
urban
|
||||
urge
|
||||
usage
|
||||
use
|
||||
used
|
||||
useful
|
||||
useless
|
||||
usual
|
||||
utility
|
||||
vacant
|
||||
vacuum
|
||||
vague
|
||||
valid
|
||||
valley
|
||||
valve
|
||||
van
|
||||
vanish
|
||||
vapor
|
||||
various
|
||||
vast
|
||||
vault
|
||||
vehicle
|
||||
velvet
|
||||
vendor
|
||||
venture
|
||||
venue
|
||||
verb
|
||||
verify
|
||||
version
|
||||
very
|
||||
vessel
|
||||
veteran
|
||||
viable
|
||||
vibrant
|
||||
vicious
|
||||
victory
|
||||
video
|
||||
view
|
||||
village
|
||||
vintage
|
||||
violin
|
||||
virtual
|
||||
virus
|
||||
visa
|
||||
visit
|
||||
visual
|
||||
vital
|
||||
vivid
|
||||
vocal
|
||||
voice
|
||||
void
|
||||
volcano
|
||||
volume
|
||||
vote
|
||||
voyage
|
||||
wage
|
||||
wagon
|
||||
wait
|
||||
walk
|
||||
wall
|
||||
walnut
|
||||
want
|
||||
warfare
|
||||
warm
|
||||
warrior
|
||||
wash
|
||||
wasp
|
||||
waste
|
||||
water
|
||||
wave
|
||||
way
|
||||
wealth
|
||||
weapon
|
||||
wear
|
||||
weasel
|
||||
weather
|
||||
web
|
||||
wedding
|
||||
weekend
|
||||
weird
|
||||
welcome
|
||||
west
|
||||
wet
|
||||
whale
|
||||
what
|
||||
wheat
|
||||
wheel
|
||||
when
|
||||
where
|
||||
whip
|
||||
whisper
|
||||
wide
|
||||
width
|
||||
wife
|
||||
wild
|
||||
will
|
||||
win
|
||||
window
|
||||
wine
|
||||
wing
|
||||
wink
|
||||
winner
|
||||
winter
|
||||
wire
|
||||
wisdom
|
||||
wise
|
||||
wish
|
||||
witness
|
||||
wolf
|
||||
woman
|
||||
wonder
|
||||
wood
|
||||
wool
|
||||
word
|
||||
work
|
||||
world
|
||||
worry
|
||||
worth
|
||||
wrap
|
||||
wreck
|
||||
wrestle
|
||||
wrist
|
||||
write
|
||||
wrong
|
||||
yard
|
||||
year
|
||||
yellow
|
||||
you
|
||||
young
|
||||
youth
|
||||
zebra
|
||||
zero
|
||||
zone
|
||||
zoo`
|
14
glide.lock
generated
14
glide.lock
generated
@ -1,6 +1,13 @@
|
||||
hash: b7b9aed5daf9b2fdc4083d310ab2cabfc86e08db6796b9301b2e2d73c9bd6174
|
||||
updated: 2018-02-23T15:57:52.662879082-08:00
|
||||
hash: 31c7557ef187f50de28557359e5179a47d5f4f153ec9b4f1ad264f771e7d1b5c
|
||||
updated: 2018-03-01T16:45:01.924542733-08:00
|
||||
imports:
|
||||
- name: git.schwanenlied.me/yawning/bsaes.git
|
||||
version: e06297f34865a50b8e473105e52cb64ad1b55da8
|
||||
subpackages:
|
||||
- ct32
|
||||
- ct64
|
||||
- ghash
|
||||
- internal/modes
|
||||
- name: github.com/aead/chacha20
|
||||
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
|
||||
subpackages:
|
||||
@ -140,9 +147,12 @@ imports:
|
||||
version: 501572607d0273fc75b3b261fa4904d63f6ffa0e
|
||||
- name: github.com/urfave/cli
|
||||
version: cfb38830724cc34fedffe9a2a29fb54fa9169cd1
|
||||
- name: github.com/Yawning/aez
|
||||
version: 4dad034d9db2caec23fb8f69b9160ae16f8d46a3
|
||||
- name: golang.org/x/crypto
|
||||
version: 49796115aa4b964c318aad4f3084fdb41e9aa067
|
||||
subpackages:
|
||||
- blake2b
|
||||
- chacha20poly1305
|
||||
- curve25519
|
||||
- ed25519
|
||||
|
@ -80,3 +80,7 @@ import:
|
||||
- package: github.com/rogpeppe/fastuuid
|
||||
- package: gopkg.in/errgo.v1
|
||||
- package: github.com/miekg/dns
|
||||
- package: github.com/Yawning/aez
|
||||
version: 4dad034d9db2caec23fb8f69b9160ae16f8d46a3
|
||||
- package: github.com/kkdai/bstream
|
||||
version: f391b8402d23024e7c0f624b31267a89998fca95
|
||||
|
Loading…
Reference in New Issue
Block a user