aezeed: re-encode salt correctly
This commit is contained in:
2 files changed
+36
-26
No files matched your search
+27
-21
@@ -70,11 +70,11 @@ const (
|
|||||||
// will result in.
|
// will result in.
|
||||||
NumMnemonicWords = 24
|
NumMnemonicWords = 24
|
||||||
|
|
||||||
// saltSize is the size of the salt we'll generate to use with scrypt
|
// 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
|
// 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
|
// role of the salt is to make the creation of rainbow tables
|
||||||
// infeasible.
|
// infeasible.
|
||||||
saltSize = 5
|
SaltSize = 5
|
||||||
|
|
||||||
// adSize is the size of the encoded associated data that will be
|
// adSize is the size of the encoded associated data that will be
|
||||||
// passed into aez when enciphering and deciphering the seed. The AD
|
// passed into aez when enciphering and deciphering the seed. The AD
|
||||||
@@ -95,7 +95,7 @@ const (
|
|||||||
|
|
||||||
// saltOffset is the index within an enciphered cipher seed that marks
|
// saltOffset is the index within an enciphered cipher seed that marks
|
||||||
// the start of the salt.
|
// the start of the salt.
|
||||||
saltOffset = EncipheredCipherSeedSize - checkSumSize - saltSize
|
saltOffset = EncipheredCipherSeedSize - checkSumSize - SaltSize
|
||||||
|
|
||||||
// checkSumSize is the index within an enciphered cipher seed that
|
// checkSumSize is the index within an enciphered cipher seed that
|
||||||
// marks the start of the checksum.
|
// marks the start of the checksum.
|
||||||
@@ -169,7 +169,7 @@ type CipherSeed struct {
|
|||||||
|
|
||||||
// salt is the salt that was used to generate the key from the user's
|
// salt is the salt that was used to generate the key from the user's
|
||||||
// specified passphrase.
|
// specified passphrase.
|
||||||
salt [saltSize]byte
|
salt [SaltSize]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// New generates a new CipherSeed instance from an optional source of entropy.
|
// New generates a new CipherSeed instance from an optional source of entropy.
|
||||||
@@ -252,7 +252,7 @@ func (c *CipherSeed) decode(r io.Reader) error {
|
|||||||
|
|
||||||
// encodeAD returns the fully encoded associated data for use when performing
|
// encodeAD returns the fully encoded associated data for use when performing
|
||||||
// our current enciphering operation. The AD is: version || salt.
|
// our current enciphering operation. The AD is: version || salt.
|
||||||
func encodeAD(version uint8, salt [saltSize]byte) [adSize]byte {
|
func encodeAD(version uint8, salt [SaltSize]byte) [adSize]byte {
|
||||||
var ad [adSize]byte
|
var ad [adSize]byte
|
||||||
ad[0] = version
|
ad[0] = version
|
||||||
copy(ad[1:], salt[:])
|
copy(ad[1:], salt[:])
|
||||||
@@ -432,14 +432,16 @@ func mnemonicToCipherText(mnemonic *Mnemonic) [EncipheredCipherSeedSize]byte {
|
|||||||
func (m *Mnemonic) ToCipherSeed(pass []byte) (*CipherSeed, error) {
|
func (m *Mnemonic) ToCipherSeed(pass []byte) (*CipherSeed, error) {
|
||||||
// First, we'll attempt to decipher the mnemonic by mapping back into
|
// First, we'll attempt to decipher the mnemonic by mapping back into
|
||||||
// our byte slice and applying our deciphering scheme.
|
// our byte slice and applying our deciphering scheme.
|
||||||
plainSeed, err := m.Decipher(pass)
|
plainSeed, salt, err := m.Decipher(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If decryption was successful, then we'll decode into a fresh
|
// If decryption was successful, then we'll decode into a fresh
|
||||||
// CipherSeed struct.
|
// CipherSeed struct.
|
||||||
var c CipherSeed
|
c := CipherSeed{
|
||||||
|
salt: salt,
|
||||||
|
}
|
||||||
if err := c.decode(bytes.NewReader(plainSeed[:])); err != nil {
|
if err := c.decode(bytes.NewReader(plainSeed[:])); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -451,21 +453,24 @@ func (m *Mnemonic) ToCipherSeed(pass []byte) (*CipherSeed, error) {
|
|||||||
// using the passed passphrase. This function is the opposite of
|
// using the passed passphrase. This function is the opposite of
|
||||||
// the encipher method.
|
// the encipher method.
|
||||||
func decipherCipherSeed(cipherSeedBytes [EncipheredCipherSeedSize]byte,
|
func decipherCipherSeed(cipherSeedBytes [EncipheredCipherSeedSize]byte,
|
||||||
pass []byte) ([DecipheredCipherSeedSize]byte, error) {
|
pass []byte) ([DecipheredCipherSeedSize]byte, [SaltSize]byte, error) {
|
||||||
|
|
||||||
var plainSeed [DecipheredCipherSeedSize]byte
|
var (
|
||||||
|
plainSeed [DecipheredCipherSeedSize]byte
|
||||||
|
salt [SaltSize]byte
|
||||||
|
)
|
||||||
|
|
||||||
// Before we do anything, we'll ensure that the version is one that we
|
// 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
|
// understand. Otherwise, we won't be able to decrypt, or even parse
|
||||||
// the cipher seed.
|
// the cipher seed.
|
||||||
if cipherSeedBytes[0] != CipherSeedVersion {
|
if cipherSeedBytes[0] != CipherSeedVersion {
|
||||||
return plainSeed, ErrIncorrectVersion
|
return plainSeed, salt, ErrIncorrectVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, we'll slice off the salt from the pass cipher seed, then
|
// 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
|
// snip off the end of the cipher seed, ignoring the version, and
|
||||||
// finally the checksum.
|
// finally the checksum.
|
||||||
salt := cipherSeedBytes[saltOffset : saltOffset+saltSize]
|
copy(salt[:], cipherSeedBytes[saltOffset:saltOffset+SaltSize])
|
||||||
cipherSeed := cipherSeedBytes[1:saltOffset]
|
cipherSeed := cipherSeedBytes[1:saltOffset]
|
||||||
checksum := cipherSeedBytes[checkSumOffset:]
|
checksum := cipherSeedBytes[checkSumOffset:]
|
||||||
|
|
||||||
@@ -475,14 +480,14 @@ func decipherCipherSeed(cipherSeedBytes [EncipheredCipherSeedSize]byte,
|
|||||||
cipherSeedBytes[:checkSumOffset], crcTable,
|
cipherSeedBytes[:checkSumOffset], crcTable,
|
||||||
)
|
)
|
||||||
if freshChecksum != binary.BigEndian.Uint32(checksum) {
|
if freshChecksum != binary.BigEndian.Uint32(checksum) {
|
||||||
return plainSeed, ErrIncorrectMnemonic
|
return plainSeed, salt, ErrIncorrectMnemonic
|
||||||
}
|
}
|
||||||
|
|
||||||
// With the salt separated from the cipher text, we'll now obtain the
|
// With the salt separated from the cipher text, we'll now obtain the
|
||||||
// key used for encryption.
|
// key used for encryption.
|
||||||
key, err := scrypt.Key(pass, salt, scryptN, scryptR, scryptP, keyLen)
|
key, err := scrypt.Key(pass, salt[:], scryptN, scryptR, scryptP, keyLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plainSeed, err
|
return plainSeed, salt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll also extract the AD that will be required to properly pass the
|
// We'll also extract the AD that will be required to properly pass the
|
||||||
@@ -496,11 +501,11 @@ func decipherCipherSeed(cipherSeedBytes [EncipheredCipherSeedSize]byte,
|
|||||||
key, nil, [][]byte{ad[:]}, CipherTextExpansion, cipherSeed, nil,
|
key, nil, [][]byte{ad[:]}, CipherTextExpansion, cipherSeed, nil,
|
||||||
)
|
)
|
||||||
if !ok {
|
if !ok {
|
||||||
return plainSeed, ErrInvalidPass
|
return plainSeed, salt, ErrInvalidPass
|
||||||
}
|
}
|
||||||
copy(plainSeed[:], plainSeedBytes)
|
copy(plainSeed[:], plainSeedBytes)
|
||||||
|
|
||||||
return plainSeed, nil
|
return plainSeed, salt, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +513,7 @@ func decipherCipherSeed(cipherSeedBytes [EncipheredCipherSeedSize]byte,
|
|||||||
// original ciphertext, then applying our deciphering scheme. ErrInvalidPass
|
// original ciphertext, then applying our deciphering scheme. ErrInvalidPass
|
||||||
// will be returned if the passphrase is incorrect.
|
// will be returned if the passphrase is incorrect.
|
||||||
func (m *Mnemonic) Decipher(pass []byte) ([DecipheredCipherSeedSize]byte,
|
func (m *Mnemonic) Decipher(pass []byte) ([DecipheredCipherSeedSize]byte,
|
||||||
error) {
|
[SaltSize]byte, error) {
|
||||||
|
|
||||||
// Before we attempt to map the mnemonic back to the original
|
// Before we attempt to map the mnemonic back to the original
|
||||||
// ciphertext, we'll ensure that all the word are actually a part of
|
// ciphertext, we'll ensure that all the word are actually a part of
|
||||||
@@ -521,10 +526,11 @@ func (m *Mnemonic) Decipher(pass []byte) ([DecipheredCipherSeedSize]byte,
|
|||||||
for i, word := range m {
|
for i, word := range m {
|
||||||
if _, ok := wordDict[word]; !ok {
|
if _, ok := wordDict[word]; !ok {
|
||||||
emptySeed := [DecipheredCipherSeedSize]byte{}
|
emptySeed := [DecipheredCipherSeedSize]byte{}
|
||||||
return emptySeed, ErrUnknownMnemonicWord{
|
return emptySeed, [SaltSize]byte{},
|
||||||
Word: word,
|
ErrUnknownMnemonicWord{
|
||||||
Index: uint8(i),
|
Word: word,
|
||||||
}
|
Index: uint8(i),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type TestVector struct {
|
|||||||
version uint8
|
version uint8
|
||||||
time time.Time
|
time time.Time
|
||||||
entropy [EntropySize]byte
|
entropy [EntropySize]byte
|
||||||
salt [saltSize]byte
|
salt [SaltSize]byte
|
||||||
password []byte
|
password []byte
|
||||||
expectedMnemonic [NumMnemonicWords]string
|
expectedMnemonic [NumMnemonicWords]string
|
||||||
expectedBirthday uint16
|
expectedBirthday uint16
|
||||||
@@ -29,7 +29,7 @@ var (
|
|||||||
0x0d, 0xe7, 0x95, 0xe4,
|
0x0d, 0xe7, 0x95, 0xe4,
|
||||||
0x1e, 0x0b, 0x4c, 0xfd,
|
0x1e, 0x0b, 0x4c, 0xfd,
|
||||||
}
|
}
|
||||||
testSalt = [saltSize]byte{
|
testSalt = [SaltSize]byte{
|
||||||
0x73, 0x61, 0x6c, 0x74, 0x31, // equal to "salt1"
|
0x73, 0x61, 0x6c, 0x74, 0x31, // equal to "salt1"
|
||||||
}
|
}
|
||||||
version0TestVectors = []TestVector{{
|
version0TestVectors = []TestVector{{
|
||||||
@@ -70,6 +70,7 @@ func assertCipherSeedEqual(t *testing.T, cipherSeed *CipherSeed,
|
|||||||
)
|
)
|
||||||
require.Equal(t, cipherSeed.Birthday, cipherSeed2.Birthday, "birthday")
|
require.Equal(t, cipherSeed.Birthday, cipherSeed2.Birthday, "birthday")
|
||||||
require.Equal(t, cipherSeed.Entropy, cipherSeed2.Entropy, "entropy")
|
require.Equal(t, cipherSeed.Entropy, cipherSeed2.Entropy, "entropy")
|
||||||
|
require.Equal(t, cipherSeed.salt, cipherSeed2.salt, "salt")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAezeedVersion0TestVectors tests some fixed test vector values against
|
// TestAezeedVersion0TestVectors tests some fixed test vector values against
|
||||||
@@ -202,12 +203,15 @@ func TestRawEncipherDecipher(t *testing.T) {
|
|||||||
|
|
||||||
// Now that we have the ciphertext (mapped to the mnemonic), we'll
|
// Now that we have the ciphertext (mapped to the mnemonic), we'll
|
||||||
// attempt to decipher it raw using the user's passphrase.
|
// attempt to decipher it raw using the user's passphrase.
|
||||||
plainSeedBytes, err := mnemonic.Decipher(pass)
|
plainSeedBytes, salt, err := mnemonic.Decipher(pass)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, cipherSeed.salt, salt)
|
||||||
|
|
||||||
// If we deserialize the plaintext seed bytes, it should exactly match
|
// If we deserialize the plaintext seed bytes, it should exactly match
|
||||||
// the original cipher seed.
|
// the original cipher seed.
|
||||||
var newSeed CipherSeed
|
newSeed := CipherSeed{
|
||||||
|
salt: salt,
|
||||||
|
}
|
||||||
err = newSeed.decode(bytes.NewReader(plainSeedBytes[:]))
|
err = newSeed.decode(bytes.NewReader(plainSeedBytes[:]))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -236,7 +240,7 @@ func TestInvalidExternalVersion(t *testing.T) {
|
|||||||
|
|
||||||
// With the version swapped, if we try to decipher it, (no matter the
|
// With the version swapped, if we try to decipher it, (no matter the
|
||||||
// passphrase), it should fail.
|
// passphrase), it should fail.
|
||||||
_, err = decipherCipherSeed(cipherText, []byte("kek"))
|
_, _, err = decipherCipherSeed(cipherText, []byte("kek"))
|
||||||
require.Equal(t, ErrIncorrectVersion, err)
|
require.Equal(t, ErrIncorrectVersion, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user