0
0
mirror of https://github.com/XTLS/REALITY.git synced 2025-08-23 15:08:37 +00:00
XTLS_REALITY/drbg/ctrdrbg.go
yuhan6665 3833e8e2cb crypto/internal/mlkem768: move to crypto/internal/fips/mlkem
In the process, replace out-of-module imports with their FIPS versions.

For #69536

Change-Id: I83e900b7c38ecf760382e5dca7fd0b1eaa5a5589
Reviewed-on: https://go-review.googlesource.com/c/go/+/626879
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2025-05-04 21:57:47 -04:00

143 lines
3.7 KiB
Go

// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package drbg
import (
//"crypto/internal/fips140"
"github.com/xtls/reality/aes"
"github.com/xtls/reality/subtle"
"github.com/xtls/reality/byteorder"
"math/bits"
)
// Counter is an SP 800-90A Rev. 1 CTR_DRBG instantiated with AES-256.
//
// Per Table 3, it has a security strength of 256 bits, a seed size of 384 bits,
// a counter length of 128 bits, a reseed interval of 2^48 requests, and a
// maximum request size of 2^19 bits (2^16 bytes, 64 KiB).
//
// We support a narrow range of parameters that fit the needs of our RNG:
// AES-256, no derivation function, no personalization string, no prediction
// resistance, and 384-bit additional input.
//
// WARNING: this type provides tightly scoped support for the DRBG
// functionality we need for FIPS 140-3 _only_. This type _should not_ be used
// outside of the FIPS 140-3 module for any other use.
//
// In particular, as documented, Counter does not support the derivation
// function, or personalization strings which are necessary for safely using
// this DRBG for generic purposes without leaking sensitive values.
type Counter struct {
// c is instantiated with K as the key and V as the counter.
c aes.CTR
reseedCounter uint64
}
const (
keySize = 256 / 8
SeedSize = keySize + aes.BlockSize
reseedInterval = 1 << 48
maxRequestSize = (1 << 19) / 8
)
func NewCounter(entropy *[SeedSize]byte) *Counter {
// CTR_DRBG_Instantiate_algorithm, per Section 10.2.1.3.1.
//fips140.RecordApproved()
K := make([]byte, keySize)
V := make([]byte, aes.BlockSize)
// V starts at 0, but is incremented in CTR_DRBG_Update before each use,
// unlike AES-CTR where it is incremented after each use.
V[len(V)-1] = 1
cipher, err := aes.New(K)
if err != nil {
panic(err)
}
c := &Counter{}
c.c = *aes.NewCTR(cipher, V)
c.update(entropy)
c.reseedCounter = 1
return c
}
func (c *Counter) update(seed *[SeedSize]byte) {
// CTR_DRBG_Update, per Section 10.2.1.2.
temp := make([]byte, SeedSize)
c.c.XORKeyStream(temp, seed[:])
K := temp[:keySize]
V := temp[keySize:]
// Again, we pre-increment V, like in NewCounter.
increment((*[aes.BlockSize]byte)(V))
cipher, err := aes.New(K)
if err != nil {
panic(err)
}
c.c = *aes.NewCTR(cipher, V)
}
func increment(v *[aes.BlockSize]byte) {
hi := byteorder.BEUint64(v[:8])
lo := byteorder.BEUint64(v[8:])
lo, c := bits.Add64(lo, 1, 0)
hi, _ = bits.Add64(hi, 0, c)
byteorder.BEPutUint64(v[:8], hi)
byteorder.BEPutUint64(v[8:], lo)
}
func (c *Counter) Reseed(entropy, additionalInput *[SeedSize]byte) {
// CTR_DRBG_Reseed_algorithm, per Section 10.2.1.4.1.
//fips140.RecordApproved()
var seed [SeedSize]byte
subtle.XORBytes(seed[:], entropy[:], additionalInput[:])
c.update(&seed)
c.reseedCounter = 1
}
// Generate produces at most maxRequestSize bytes of random data in out.
func (c *Counter) Generate(out []byte, additionalInput *[SeedSize]byte) (reseedRequired bool) {
// CTR_DRBG_Generate_algorithm, per Section 10.2.1.5.1.
//fips140.RecordApproved()
if len(out) > maxRequestSize {
panic("crypto/drbg: internal error: request size exceeds maximum")
}
// Step 1.
if c.reseedCounter > reseedInterval {
return true
}
// Step 2.
if additionalInput != nil {
c.update(additionalInput)
} else {
// If the additional input is null, the first CTR_DRBG_Update is
// skipped, but the additional input is replaced with an all-zero string
// for the second CTR_DRBG_Update.
additionalInput = new([SeedSize]byte)
}
// Steps 3-5.
clear(out)
c.c.XORKeyStream(out, out)
aes.RoundToBlock(&c.c)
// Step 6.
c.update(additionalInput)
// Step 7.
c.reseedCounter++
// Step 8.
return false
}