mirror of
https://github.com/XTLS/REALITY.git
synced 2025-08-23 15:08:37 +00:00
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>
143 lines
3.7 KiB
Go
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
|
|
} |