add comments and enhance gm validation

This commit is contained in:
Sun Yimin 2022-10-21 10:22:10 +08:00 committed by GitHub
parent 7f86563f74
commit 0342ada322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 52 deletions

View File

@ -15,9 +15,11 @@ import (
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST uint64 = 8
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20
const DRBG_RESEED_TIME_INTERVAL_LEVEL_TEST = time.Duration(6) * time.Second
const DRBG_RESEED_TIME_INTERVAL_LEVEL2 = time.Duration(60) * time.Second
const DRBG_RESEED_TIME_INTERVAL_LEVEL1 = time.Duration(600) * time.Second
const MAX_BYTES = 1 << 27
const MAX_BYTES_PER_GENERATE = 1 << 11
@ -46,7 +48,11 @@ func NewCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLe
} else {
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
if gm && securityStrength < 32 {
return nil, errors.New("invalid security strength")
}
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
@ -89,6 +95,9 @@ func NewHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
if gm && securityStrength < 32 {
return nil, errors.New("invalid security strength")
}
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
@ -97,7 +106,7 @@ func NewHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int
return nil, err
}
// Get nonce
// Get nonce from entropy source here
nonce := make([]byte, prng.securityStrength/2)
err = prng.getEntropy(nonce)
if err != nil {
@ -213,8 +222,10 @@ func selectSecurityStrength(requested int) int {
return 16
case requested <= 24:
return 24
default:
case requested <= 32:
return 32
default:
return requested
}
}

View File

@ -7,7 +7,7 @@ import (
)
func TestGmCtrDrbgPrng(t *testing.T) {
prng, err := NewGmCtrDrbgPrng(nil, 16, SECURITY_LEVEL_TEST, nil)
prng, err := NewGmCtrDrbgPrng(nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}

View File

@ -25,12 +25,12 @@ func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen in
hd.setSecurityLevel(securityLevel)
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || len(entropy) >= MAX_BYTES {
if len(entropy) == 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= MAX_BYTES {
return nil, errors.New("invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) <= 0 || len(nonce) >= MAX_BYTES>>1 {
if len(nonce) == 0 || (hd.gm && len(entropy) < 16) || len(nonce) >= MAX_BYTES>>1 {
return nil, errors.New("invalid nonce length")
}
@ -49,12 +49,16 @@ func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen in
hd.v = make([]byte, block.BlockSize())
hd.key = make([]byte, hd.keyLen)
// seed_material = entropy_input || instantiation_nonce || personalization_string
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
seed := hd.derive(seedMaterial, hd.seedLength)
hd.update(seed)
// seed_material = Block_Cipher_df(seed_material, seed_length)
seedMaterial = hd.derive(seedMaterial, hd.seedLength)
// CTR_DRBG_Updae(seed_material, Key, V)
hd.update(seedMaterial)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
@ -72,7 +76,7 @@ func NewGMCtrDrbg(securityLevel SecurityLevel, entropy, nonce, personalization [
func (hd *CtrDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || len(entropy) >= MAX_BYTES {
if len(entropy) <= 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= MAX_BYTES {
return errors.New("invalid entropy length")
}
@ -80,6 +84,7 @@ func (hd *CtrDrbg) Reseed(entropy, additional []byte) error {
return errors.New("additional input too long")
}
// seed_material = entropy_input || additional_input
var seedMaterial []byte
if len(additional) == 0 {
seedMaterial = entropy
@ -88,8 +93,11 @@ func (hd *CtrDrbg) Reseed(entropy, additional []byte) error {
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], additional)
}
seed := hd.derive(seedMaterial, hd.seedLength)
hd.update(seed)
// seed_material = Block_Cipher_df(seed_material, seed_length)
seedMaterial = hd.derive(seedMaterial, hd.seedLength)
// CTR_DRBG_Updae(seed_material, Key, V)
hd.update(seedMaterial)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
@ -110,7 +118,7 @@ func (hd *CtrDrbg) MaxBytesPerRequest() int {
return MAX_BYTES_PER_GENERATE
}
// Generate CTR DRBG generate process.
// Generate CTR DRBG pseudorandom bits generate process.
func (hd *CtrDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
return ErrReseedRequired
@ -120,6 +128,9 @@ func (hd *CtrDrbg) Generate(b, additional []byte) error {
return errors.New("too many bytes requested")
}
// If len(additional_input) > 0, then
// additional_input = Block_Cipher_df(additional_input, seed_length)
// CTR_DRBG_Update(additional_input, Key, V)
if len(additional) > 0 {
additional = hd.derive(additional, hd.seedLength)
hd.update(additional)
@ -131,7 +142,9 @@ func (hd *CtrDrbg) Generate(b, additional []byte) error {
m := len(b)
limit := uint64(m+outlen-1) / uint64(outlen)
for i := 0; i < int(limit); i++ {
// V = (V + 1) mod 2^outlen)
addOne(hd.v, outlen)
// output_block = Encrypt(Key, V)
block.Encrypt(temp, hd.v)
copy(b[i*outlen:], temp)
}
@ -149,20 +162,28 @@ func (cd *CtrDrbg) update(seedMaterial []byte) {
output := make([]byte, outlen)
copy(v, cd.v)
for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ {
// V = (V + 1) mod 2^outlen
addOne(v, outlen)
// output_block = Encrypt(Key, V)
block.Encrypt(output, v)
copy(temp[i*outlen:], output)
}
// temp = temp XOR seed_material
subtle.XORBytes(temp, temp, seedMaterial)
// Key = leftmost(temp, key_length)
copy(cd.key, temp)
// V = rightmost(temp, outlen)
copy(cd.v, temp[cd.keyLen:])
}
// derive Block_Cipher_df
func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
outlen := cd.seedLength - cd.keyLen
lenS := ((4 + 4 + len(seedMaterial) + outlen) / outlen) * outlen
S := make([]byte, lenS+outlen)
// S = counter || len(seed_material) || len(return_bytes) || seed_material || 0x80
// len(S) = ((outlen + 4 + 4 + len(seed_material) + 1 + outlen - 1) / outlen) * outlen
binary.BigEndian.PutUint32(S[outlen:], uint32(len(seedMaterial)))
binary.BigEndian.PutUint32(S[outlen+4:], uint32(returnBytes))
copy(S[outlen+8:], seedMaterial)

View File

@ -182,43 +182,43 @@ var ctrtests = []struct {
true,
sm4.NewCipher,
16,
"0f65da13dca407999d4773c2b4a11d85",
"5209e5b4ed82a234",
"2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5",
"0bf814b411f65ec4866be1abb59d3c32",
"",
"3dee0b770815026b88a86a1637c4c9f6",
"40ef052e0dffc441fd644f6dce7430c0",
"1dea0a12c52bf64339dd291c80d8ca89",
"044f9ff3b7e8ad2b60a7b2c05fe6b5b7",
"7fce60b97d8ceb60506bff1d37b1a936",
"93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207",
"",
"1c5c3a1369b7ab6ea6f5631b0a8e4f2d", // v1
"ac5783e385d6d4c6a2c5a184e7cebecb", //key1
"8bd44b2e39f8186497f889c73555797d", // v1
"02b9a8f88124bd9cec909e1fd7ec9971", //key1
"",
"22b16dfe04896ef1ddf69f12e5d9a1dd", // v2
"3f7b6539fc274b97565b2a1b26d021e0", //key2
"fbc91ad876ba3a84588be2f358b9e13c", // v2
"4804b2a1a971ca729abff5bada051cf6", //key2
"",
"58129d515c9bb6d32e1ab1206fe6c618",
"69bfff2ab5f3dbcc5eaca7eb5a3cf8e4",
"c2b4a693df1f768e7d45d0926a40f527",
"e732a524de8ad239aa293ac8ae588f9d",
"ce60250d77048bdbe48ade354b6869f6",
"6788e31ae27aae09a14aed967ce8b219",
},
{ // SM4-128, with additional input
false,
sm4.NewCipher,
16,
"285da6cf762552634636bfee3400b156",
"8f8bada74820cb43",
"6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e",
"401e3f87762fa8a14ab232ccb8480a2f",
"",
"f5cb0d67b1784b97b1d90fff63e67d32", // v0
"f55a8ee6f9414feac90fe15a43c9bc11", // key0
"b4699b33354a83bfed115f770f32db0b", // EntropyInputReseed
"38bfec9a10e6e40c106841dae48dc3b8", // AdditionalInputReseed
"7398bf677878ae483b0e560b9c5bf666", // v1
"402b8f4743e52ba2d7dc88541f5d23b1", // key1
"629ead5bacfac8235711ffeb22f57558", // AdditionalInput1
"095162afa9927d43e1bb531ff9719a32", // v2
"c8a759bc36a5b86ede53122a89041a1c", // key2
"dd8a02ee668ca3e03949b38cb6e6b4df", // AdditionalInput2
"71f9fbd6bc78149061eabfbd248f54eb",
"eddc1b09ff7ce31f19a0922249d78345", // v3
"8c10825d503632f8cabed20bc62d6a08", // key3
"5e8c10afe142dc9c8caf35411b38730a", // v0
"d72aefa9fd527383ad418f6158627feb", // key0
"350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed
"7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed
"c00836da0fd780cdc81dabec80e344ce", // v1
"f5f3abdeff30df22f4866d83cd96bc1b", // key1
"67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1
"6ddb205ec76567b31a07ee48437acebc", // v2
"5e23cbe8b97065102ca0d87bfd9ae0da", // key2
"648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2
"b0ac91f148efbdc3570d7e434aba8d24",
"d1f029bb089613d836ddc6fe1d6fb96f", // v3
"8adfe65e9137b18f060ae91e7a6224c1", // key3
},
}
@ -231,7 +231,7 @@ func TestCtrDRBG(t *testing.T) {
key0, _ := hex.DecodeString(test.key0)
hd, err := NewCtrDrbg(test.cipherProvider, test.keyLen, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Error(err)
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("case %v, not same v0 %s", i+1, hex.EncodeToString(hd.v))
@ -246,7 +246,7 @@ func TestCtrDRBG(t *testing.T) {
key1, _ := hex.DecodeString(test.key1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Error(err)
t.Fatal(err)
}
if !bytes.Equal(hd.v, v1) {
t.Errorf("case %v, not same v1 %s", i+1, hex.EncodeToString(hd.v))

View File

@ -23,15 +23,16 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
hd := &HashDrbg{}
hd.gm = gm
hd.md = md
hd.setSecurityLevel(securityLevel)
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || len(entropy) >= MAX_BYTES {
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.md.Size()) || len(entropy) >= MAX_BYTES {
return nil, errors.New("invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) <= 0 || len(nonce) >= MAX_BYTES>>1 {
if len(nonce) == 0 || (hd.gm && len(entropy) < hd.md.Size()/2) || len(nonce) >= MAX_BYTES>>1 {
return nil, errors.New("invalid nonce length")
}
@ -39,7 +40,6 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
return nil, errors.New("personalization is too long")
}
hd.md = md
if md.Size() <= sm3.Size {
hd.v = make([]byte, HASH_DRBG_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_SEED_SIZE)
@ -49,17 +49,24 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
hd.c = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
hd.seedLength = HASH_DRBG_MAX_SEED_SIZE
}
// seed_material = entropy_input || instantiation_nonce || personalization_string
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
// seed = Hash_df(seed_material, seed_length)
seed := hd.derive(seedMaterial, hd.seedLength)
// V = seed
copy(hd.v, seed)
// C = Hash_df(0x00 || V, seed_length)
temp := make([]byte, hd.seedLength+1)
temp[0] = 0
copy(temp[1:], seed)
seed = hd.derive(temp, hd.seedLength)
copy(hd.c, seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
@ -79,7 +86,7 @@ func NewGMHashDrbg(securityLevel SecurityLevel, entropy, nonce, personalization
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || len(entropy) >= MAX_BYTES {
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.md.Size()) || len(entropy) >= MAX_BYTES {
return errors.New("invalid entropy length")
}
@ -88,23 +95,28 @@ func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
}
seedMaterial := make([]byte, len(entropy)+hd.seedLength+len(additional)+1)
seedMaterial[0] = 1
if hd.gm { // entropy_input || V || additional_input
if hd.gm { // seed_material = 0x01 || entropy_input || V || additional_input
copy(seedMaterial[1:], entropy)
copy(seedMaterial[len(entropy)+1:], hd.v)
} else { // V || entropy_input || additional_input
} else { // seed_material = 0x01 || V || entropy_input || additional_input
copy(seedMaterial[1:], hd.v)
copy(seedMaterial[hd.seedLength+1:], entropy)
}
copy(seedMaterial[len(entropy)+hd.seedLength+1:], additional)
// seed = Hash_df(seed_material, seed_length)
seed := hd.derive(seedMaterial, hd.seedLength)
// V = seed
copy(hd.v, seed)
temp := make([]byte, hd.seedLength+1)
// C = Hash_df(0x01 || V, seed_length)
temp[0] = 0
copy(temp[1:], seed)
seed = hd.derive(temp, hd.seedLength)
copy(hd.c, seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
@ -140,7 +152,7 @@ func (hd *HashDrbg) MaxBytesPerRequest() int {
return MAX_BYTES_PER_GENERATE
}
// Generate hash DRBG generate process. GM/T 0105-2021 has a little different with NIST.
// Generate hash DRBG pseudorandom bits process. GM/T 0105-2021 has a little different with NIST.
// GM/T 0105-2021 can only generate no more than hash.Size bytes once.
func (hd *HashDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
@ -151,6 +163,9 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
}
md := hd.md
m := len(b)
// if len(additional_input) > 0, then
// w = Hash(0x02 || V || additional_input)
if len(additional) > 0 {
md.Write([]byte{0x02})
md.Write(hd.v)
@ -159,7 +174,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
md.Reset()
hd.addW(w)
}
if hd.gm { // leftmost(HASH(V))
if hd.gm { // leftmost(Hash(V))
md.Write(hd.v)
copy(b, md.Sum(nil))
md.Reset()
@ -174,6 +189,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
md.Reset()
}
}
// V = (V + H + C + reseed_counter) mode 2^seed_length
hd.addH()
hd.addC()
hd.addReseedCounter()
@ -182,6 +198,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
return nil
}
// derive Hash_df
func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
md := hd.md
limit := uint64(len+md.Size()-1) / uint64(md.Size())
@ -190,6 +207,7 @@ func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
var ct byte = 1
k := make([]byte, len)
for i := 0; i < int(limit); i++ {
// Hash( counter_byte || return_bits || seed_material )
md.Write([]byte{ct})
md.Write(requireBytes[:])
md.Write(seedMaterial)

View File

@ -185,7 +185,7 @@ func TestHashDRBG(t *testing.T) {
c0, _ := hex.DecodeString(test.c0)
hd, err := NewHashDrbg(test.md, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Error(err)
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("not same v0 %s", hex.EncodeToString(hd.v[:len(v0)]))
@ -200,7 +200,7 @@ func TestHashDRBG(t *testing.T) {
c1, _ := hex.DecodeString(test.c1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Error(err)
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v1) {
t.Errorf("not same v1 %s", hex.EncodeToString(hd.v[:len(v0)]))