awnumar/memguard

Implement io.Reader, io.Writer, etc.

au-phiware opened this issue · 4 comments

Is your feature request related to a problem? Please describe.
Some APIs that work with sensitive data use the io interfaces, e.g. openpgp.ReadKeyRing and openpgp.(*Entity).SerializePrivate.

Describe the solution you'd like
It would be easier to use memguard if LockedBuffer implemented these interfaces. Particularly for io.Writer, since in this case the LockedBuffer could handle growing the buffer as needed.

Describe alternatives you've considered
At the moment I work around this issue by wrapping LockedBuffer.Buffer() in a bytes.Buffer. However, this causes a problem when the buffer needs to grow.

The growing of the buffer will always be difficult (see here) unless I can come up with a way to do it seamlessly. I think currently the only way to do it in memguard is by creating a new container of the appropriate size, moving the data, and then destroying the old container.

I've read that article previously; it's very interesting. I figured that a new buffer would need to be allocated and all the contents copied across. I think the cost is appropriate for the benefit.

Prevention is better than cure in this case. For me I'll know the required buffer size 99% of the time I just want something to cover that 1%.

An easy solution for this would be for me to just add a single new method. Something like,

// Grow increases the size of an Enclave by a given number of bytes.
func (b *Enclave) Grow(n int) (*Enclave, error) {}

I committed a Resize method for LockedBuffer objects:

memguard/buffer.go

Lines 170 to 195 in 5583953

/*
Resize allocates a new buffer of a positive integer size strictly greater than zero, copies the data and mutability attribute over from the old one before destroying it.
*/
func (b *LockedBuffer) Resize(size int) (*LockedBuffer, error) {
if !b.IsAlive() {
return nil, core.ErrDestroyed
}
b.RLock()
new, err := NewBuffer(size)
if err != nil {
return nil, err
}
crypto.Move(new.Buffer.Data, b.Buffer.Data)
if !b.IsMutable() {
new.Freeze()
}
b.RUnlock()
b.Destroy()
return new, nil
}

Is this sufficient? If not, please explain why.