Author of `go.secrets` here (it's defunct, btw). Memguard, at first glance, will not actually do anything. Why? Because go's garbage collector can and will move and copy memory as it sees fit. `mlock` on a go-managed buffer just prevents the original memory location from being swapped out to disk, but does nothing to the copies that the go runtime will periodically create.
This is why I used libsodium (I also implemented this same concept, much more maturely for Rust[1]). If you want this approach to work, you have to manage the memory yourself.
In the Rust version, I also use Rust's ownership rules to automatically `mprotect` with `PROT_NONE` when it's not in use, `PROT_READ` when it's being borrowed immutably, and `PROT_WRITE` when it's being borrowed mutably, all with static compilation guarantees. Plus libsodium creates guard pages before and after the allocation (ensuring no underflows or overflows either into or out of the allocated memory space), and also places a canary before the allocated region that panics when the memory is freed if the canary has been modified. It's far, far more than a simple `mlock`.
I have a rewrite half-in-progress[2] that handles stack-allocated secrets with fewer guarantees (`mlock` and zero-on-free) but that's more appropriate for short-lived stack secrets.
You could, but then you still have the original data in the original location which needs to be scrubbed. And go does not, to my knowledge, provide semantics that allow for doing this in a way that should be considered cryptographically reliable.
The go runtime might optimize away your memzero, or it could have created other copies that you don't have a handle to.
In the Rust version of my library (and maybe in the go version, it's been ages since I worked on it), I go out of my way to make it difficult to copy data from runtime-managed memory into a secret buffer. You can do this, and it makes a best-effort attempt at zeroing the data when you do, but you lose a lot of hard guarantees when you do.
The GP specifically mentioned copying data into an `mmap`'d buffer. My point is that copying presupposes secrets already in managed memory, and at that point you've already lost.
First, the fact that go's language specification allows for this should be enough to stop you right there. Even if today it doesn't move memory around, an update very well could. This library is supposed to be used for cryptographic secrets; "it works by accident for now, probably" is not even close to the kind of situation you should be designing an API around. At any point, without serious warning, an update to the golang runtime can render these protections useless.
There are situations today where go will move data on the stack. I am unsure if it will move heap allocations, but when the garbage collector adds compaction support this will absolutely be the case.
This is why I used libsodium (I also implemented this same concept, much more maturely for Rust[1]). If you want this approach to work, you have to manage the memory yourself.
In the Rust version, I also use Rust's ownership rules to automatically `mprotect` with `PROT_NONE` when it's not in use, `PROT_READ` when it's being borrowed immutably, and `PROT_WRITE` when it's being borrowed mutably, all with static compilation guarantees. Plus libsodium creates guard pages before and after the allocation (ensuring no underflows or overflows either into or out of the allocated memory space), and also places a canary before the allocated region that panics when the memory is freed if the canary has been modified. It's far, far more than a simple `mlock`.
I have a rewrite half-in-progress[2] that handles stack-allocated secrets with fewer guarantees (`mlock` and zero-on-free) but that's more appropriate for short-lived stack secrets.
[1]: https://github.com/stouset/secrets
[2]: https://github.com/stouset/secrets/tree/stack-secrets