Open FiloSottile opened 6 months ago
No change in consensus, so accepted. 🎉 This issue now tracks the work of implementing the proposal. — rsc for the proposal review group
The proposal is to add to crypto/rand a single function:
// Text returns a cryptographically random string using the standard RFC 4648 base32 alphabet
// for use when a secret string, token, password, or other text is needed.
// The result contains at least 128 bits of randomness, enough to prevent brute force
// guessing attacks and to make the likelihood of collisions vanishingly small.
// A future version may return longer texts as needed to maintain those properties.
func Text() string
Other features like custom alphabets and custom lengths are deferred to future proposals once we see how well Text works.
Apologies for the late response.
It is unclear to me what the intended use cases for this fixed-format Text
function are. RSC lamented a lack of uses for custom alphabets, to which several people responded with ideas. Where are the use cases for a fixed alphabet? Personally, I can think of a few hypothetical cases where i may want a string sampled from a particular alphabet, but none where i don't care what the alphabet or length are, and none where i would specifically want it to be base32. The original proposal mentioned "passwords, bearer tokens, and 2FA codes", but as https://github.com/golang/go/issues/67057#issuecomment-2164149580 explained, none of these seem well-served by the latest proposal.
If this must be added, i would prefer it be called rand.Base32
. It seems unlikely that the character set can ever be changed once it is added; expanding the character set may break applications which assume it is base32; reducing it would make the strings longer for little benefit. rand.Text
is a good, general name and it seems a shame to use it up on such a special case.
Assuming someone wants 128 bits of random base32 text (or any other standard encoding), it seems easy enough to get it:
var buf = make([]byte, 128/8)
rand.Read(buf)
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(buf)
The main difference being that, in this snippet, the final character will have only 3 bits of entropy instead of 5, whereas the string returned from Text would sample every character evenly. The proposal fails to motivate why this last-character bias is important enough to address. (And if it is that important, why is it not mentioned in the doc comment?).
The main difference being that, in this snippet, the final character will have only 3 bits of entropy instead of 5, whereas the string returned from Text would sample every character evenly. The proposal fails to motivate why this last-character bias is important enough to address.
This is not the "bias" mentioned in the proposal. The "bias" is people using math/rand
or people writing code with modulo bias:
var s string
for len(s) < 11 {
var buf [8]uint64
rand.Read(buf[:])
v := binary.LittleEndian.Uint64(buf[:])
// INSECURE: modulo bias.
s += alphabet[v % len(alphabet)]
}
The snippet you posted is indeed secure since the encoding E(x) -> s outputs a cryptographically secure string if x is cryptographically secure and E is injective.
The proposed name change to rand.Base32()
seems fine to me.
Of "passwords, bearer tokens, and 2FA codes" this doesn't seem like a fit for passwords (too many arbitrary requirements) or 2FA codes (too hard to type) but it's perfect for a bearer token, and that's a pretty common need.
The proposal was accepted as rand.Text. That's still a good name.
This is accepted and the freeze is starting soon. Is anyone working on getting it in before the freeze?
Shh — i was hoping that everyone had forgotten. ;)
I don't believe anyone from our team is working on it. If anyone wants to send a CL for it we can probably review it before the freeze.
Change https://go.dev/cl/627477 mentions this issue: crypto/rand: add Text for secure random strings
Update, Jun 27 2024: The current proposal is https://github.com/golang/go/issues/67057#issuecomment-2161221119
Random strings are useful as passwords, bearer tokens, and 2FA codes.
Generating them without bias from crypto/rand is not trivial at all, and applications are lured into using math/rand for it. See https://github.com/greenpau/caddy-security/issues/265 for example.
I propose we add a top-level function that takes a charset and returns a string of elements randomly selected from it.
The length of the string is selected automatically to provide 128 bits of security. If uppercase and lowercase letters and digits are used, a string will be
ceil(log62(2^128))
= 22 characters long.If more than 2^48 strings are generated, the chance of collision becomes higher than 2^32, but that's also true of UUID v4. Callers that reach those scales could call String twice and concatenate the results, but it doesn't feel worth documenting.
There is no error return value on the assumption that #66821 is accepted. That allows convenient slicing if necessary.
This can't really be implemented in constant time, but since it always runs randomly, attackers can get only a single timing sample, which limits the maximum theoretical leak to a few bits.
Do we already have constants for the most common charsets defined somewhere?
/cc @golang/security @golang/proposal-review