mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
aezeed: make seed generation fully deterministic
This commit fixes a TODO to make the seed generation fully deterministic by providing the option to pass in a custom randomness source.
This commit is contained in:
parent
63e28a27b8
commit
daa5966119
@ -128,6 +128,33 @@ var (
|
||||
BitcoinGenesisDate = time.Unix(1231006505, 0)
|
||||
)
|
||||
|
||||
// SeedOptions is a type that holds options that configure the generation of a
|
||||
// new cipher seed.
|
||||
type SeedOptions struct {
|
||||
// randomnessSource is the source of randomness that is used to generate
|
||||
// the salt that is used for encrypting the seed.
|
||||
randomnessSource io.Reader
|
||||
}
|
||||
|
||||
// DefaultOptions returns the default seed options.
|
||||
func DefaultOptions() *SeedOptions {
|
||||
return &SeedOptions{
|
||||
randomnessSource: rand.Reader,
|
||||
}
|
||||
}
|
||||
|
||||
// SeedOptionModifier is a function signature for modifying the default
|
||||
// SeedOptions.
|
||||
type SeedOptionModifier func(*SeedOptions)
|
||||
|
||||
// WithRandomnessSource returns an option modifier that replaces the default
|
||||
// randomness source with the given reader.
|
||||
func WithRandomnessSource(src io.Reader) SeedOptionModifier {
|
||||
return func(opts *SeedOptions) {
|
||||
opts.randomnessSource = src
|
||||
}
|
||||
}
|
||||
|
||||
// CipherSeed is a fully decoded instance of the aezeed scheme. At a high
|
||||
// level, the encoded cipher seed is the enciphering of: a version byte, a set
|
||||
// of bytes for a timestamp, the entropy which will be used to directly
|
||||
@ -174,17 +201,22 @@ type CipherSeed struct {
|
||||
|
||||
// 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.
|
||||
// place. The final fixed argument should be the time at which the seed was
|
||||
// created, followed by optional seed option modifiers.
|
||||
func New(internalVersion uint8, entropy *[EntropySize]byte,
|
||||
now time.Time) (*CipherSeed, error) {
|
||||
now time.Time, modifiers ...SeedOptionModifier) (*CipherSeed, error) {
|
||||
|
||||
// TODO(roasbeef): pass randomness source? to make fully deterministic?
|
||||
opts := DefaultOptions()
|
||||
for _, modifier := range modifiers {
|
||||
modifier(opts)
|
||||
}
|
||||
|
||||
// If a set of entropy wasn't provided, then we'll read a set of bytes
|
||||
// from the CSPRNG of our operating platform.
|
||||
// from the randomness source provided (which by default is the system's
|
||||
// CSPRNG).
|
||||
var seed [EntropySize]byte
|
||||
if entropy == nil {
|
||||
if _, err := rand.Read(seed[:]); err != nil {
|
||||
if _, err := opts.randomnessSource.Read(seed[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
@ -205,7 +237,7 @@ func New(internalVersion uint8, entropy *[EntropySize]byte,
|
||||
|
||||
// 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 {
|
||||
if _, err := opts.randomnessSource.Read(c.salt[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,35 @@ func TestAezeedVersion0TestVectors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithRandomnessSource tests that seed generation is fully deterministic
|
||||
// when a custom static randomness source is provided.
|
||||
func TestWithRandomnessSource(t *testing.T) {
|
||||
sourceData := append([]byte{}, testEntropy[:]...)
|
||||
sourceData = append(sourceData, testSalt[:]...)
|
||||
src := bytes.NewReader(sourceData)
|
||||
|
||||
// First, we create new cipher seed with the given values from the test
|
||||
// vector but with no entropy.
|
||||
v := version0TestVectors[0]
|
||||
cipherSeed, err := New(
|
||||
v.version, nil, v.time, WithRandomnessSource(src),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The salt should be set to our test salt.
|
||||
require.Equal(t, testSalt, cipherSeed.salt)
|
||||
|
||||
// Now that the seed has been created, we'll attempt to convert it to a
|
||||
// valid mnemonic.
|
||||
mnemonic, err := cipherSeed.ToMnemonic(v.password)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Finally, we compare the generated mnemonic and birthday to the
|
||||
// expected value.
|
||||
require.Equal(t, v.expectedMnemonic[:], mnemonic[:])
|
||||
require.Equal(t, v.expectedBirthday, cipherSeed.Birthday)
|
||||
}
|
||||
|
||||
// TestEmptyPassphraseDerivation tests that the aezeed scheme is able to derive
|
||||
// a proper mnemonic, and decipher that mnemonic when the user uses an empty
|
||||
// passphrase.
|
||||
|
Loading…
Reference in New Issue
Block a user