Skip to content

crypto/rand: Documentation for non-specialists #54979

Closed as not planned
Closed as not planned
@Deleplace

Description

@Deleplace

The crypto/rand package contains 1 documented exported global object "Reader", 3 documented exported funcs, and 1 full example showing how to produce 10 random bytes.

It may be non-trivial for some developers to correctly derive common use cases from this API and this example.

It seems that we may have 2 opposite approaches A and B about how we want the crypto/rand package to be used:

A. Crypto is subtle in general. If one is not sure of what they're doing, they shouldn't use the crypto/rand package, risking to use it incorrectly and getting a false feeling of security. This is more or less the warning of the crypto/subtle, applied to all things crypto. So our best advice is to discourage many developers from using crypto/rand.

B. Generating sensitive secrets (nonces, passwords) is a common use case, for which using math/rand is mostly incorrect. math/rand has a useful warning about this "This package's outputs might be easily predictable regardless of how it's seeded. For random numbers suitable for security-sensitive work, see the crypto/rand package." We want to strongly discourage math/rand for sensitive numbers, thus we expect developers to use crypto/rand and encourage them to do so.

The following assumes that our approach is B, not A.

math/rand and crypto/rand have a different API, and sometimes what we want is the convenience and features of the "math" API, with the security provided by the "crypto" package.

As a contrived but realistic example, let's say a non-cryptographer needs to generate a secure random string of 16 characters, where each character is in range 'a'-'z', uniform, per some 3rd party API requirements.

This would be straightforward with the math/rand API:

var alphabet = "abcdefghijklmnopqrstuvwxyz"

func generateNonce() string {
	a := make([]byte, 16)
	for i := range a {
		a[i] = alphabet[rand.Intn(len(alphabet))]
	}
	return string(a)
}

and more complicated with the crypto/rand API:

func generateNonce() (string, error) {
	a := make([]byte, 16)
	for i := range a {
		bi, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphabet))))
		if err != nil {
			return "", err
		}
		k := int(bi.Int64())
		a[i] = alphabet[k]
	}
	return string(a), nil
}

Even worse, the developer could use incorrect shortcuts in good faith, without noticing:

func generateNonce() (string, error) {
	a := make([]byte, 16)
	_, err := rand.Read(a)
	if err != nil {
		return "", err
	}
	for i := range a {
		k := a[i] % byte(len(alphabet))     // <- probable bug here
		a[i] = alphabet[k]
	}
	return string(a), nil
}

(this distribution is skewed, which could be bad for security, though I'm not sure how bad exactly)

My suggestion is to embrace B and enrich the documentation of crypto/rand with vetted examples for a few use cases. Before writing any, I'd like to discuss the idea.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DocumentationIssues describing a change to documentation.FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions