awnumar/memguard

Support custom randomness source

olabini opened this issue · 10 comments

Is your feature request related to a problem? Please describe.
When testing cryptographic systems it's always useful to be able to have a predictable source of randomness so you can support test vectors. For NewImmutableRandom, NewMutableRandom, FillRandomBytes, FillRandomBytesAt it would be nice to be able to provide predictable randomness during a test phase.

Describe the solution you'd like
One way of solving the problem would be to have a way of temporarily setting a rand Reader for the package temporarily. Another solution would be that each LockedBuffer has an io.Reader associated with it, that can be set (although that doesn't help for NewImmutableRandom and NewMutableRandom. Another solution would be to add functions that take a Reader for the randomness source.

Describe alternatives you've considered
I don't really have any good alternatives to how to solve this.

Introducing weak cryptographic randomness as a first-class feature of the API seems unwise. If you need some predictable random data you can generate it yourself and store it ready to be supplied to any test routines.

Here's an example:

func TestHash(t *testing.T) {
known := make(map[string]string)
known[""] = "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g="
known["hash"] = "l+2qaVlkOBNtzRKFU+kEvAP1JkJvcn0nC2mEH7bPUNM="
known["test"] = "kosgNmlD4q/RHrwOri5TqTvxd6T881vMZNUDcE5l4gI="
for k, v := range known {
if base64.StdEncoding.EncodeToString(crypto.Hash([]byte(k))) != v {
t.Error("digest doesn't match known values")
}
}
}

The problem is that this approach doesn't really work for testing implementations that use Memguard. For example, I want to use Memguard to make an implementation of elliptic curves work more safely. The Memguard LockedBuffer would be the underlying data store instead of a byte-slice. Let's take the functionality of generating a new key. The first step is to generate some random data, then flip some bits and clamp the value. In order to test that key-generation works, I would need to control the randomness going into the generation.

These kinds of problems show up all over the place - it's basically impossible to test cryptographic APIs well, without controlling the randomness in some place.

Have you considered calling NewImmutableFromBytes with some random data from a source of your own choosing? If this is an inadequate solution, please explain why.

Alternatively we could add some global randomness interface that defaults to the operating system's trusted source of random data but can be supplied with and use an alternative Reader object similar to crypto/rand::Read.

I was thinking about adding that - but my problem is that if you do that, the default case would be that randomness from rand.Reader gets generated into a byte-slice, and then sent to the LockedBuffer. The problem with that is that now the randomness from the rand.Reader is floating around in an unprotected buffer, thus defeating the purpose of using Memguard in the first place.

What about something like:

buf, _ := memguard.NewMutable(32)
reader := myCustomReader()
if _, err := reader.Read(buf.Bytes()); err != nil {
    Panic(err)
}

This way the Reader streams the data directly into the Buffer supplied by memguard.

I don't know how the internals of Memguard works. Is this as safe as what the internal randomness functions are doing? In that case that should be fine, yes.

Thanks.

Well LockedBuffer::Bytes returns a byte slice pointing to the allocated buffer. I assume it's up to the particular Reader to properly implement the streaming of data to the correct place.

Sure - my question is just whether this is the same as the Memguard randomness functions do internally, or if there's additional protection happening there.

The internal routines do exactly the same thing using crypto/rand.

OK, great. In that case I'll do that instead. You can close this issue if you want.