diff --git a/drbg/common.go b/drbg/common.go index 18de22d..ec0673c 100644 --- a/drbg/common.go +++ b/drbg/common.go @@ -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 } } diff --git a/drbg/common_test.go b/drbg/common_test.go index bc8dc42..548efd0 100644 --- a/drbg/common_test.go +++ b/drbg/common_test.go @@ -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) } diff --git a/drbg/ctr_drbg.go b/drbg/ctr_drbg.go index 98503c6..81c8255 100644 --- a/drbg/ctr_drbg.go +++ b/drbg/ctr_drbg.go @@ -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) diff --git a/drbg/ctr_drbg_test.go b/drbg/ctr_drbg_test.go index 2b53290..aa85d6b 100644 --- a/drbg/ctr_drbg_test.go +++ b/drbg/ctr_drbg_test.go @@ -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)) diff --git a/drbg/hash_drbg.go b/drbg/hash_drbg.go index edbe05a..adb23dd 100644 --- a/drbg/hash_drbg.go +++ b/drbg/hash_drbg.go @@ -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) diff --git a/drbg/hash_drbg_test.go b/drbg/hash_drbg_test.go index dfad8a8..c2107b9 100644 --- a/drbg/hash_drbg_test.go +++ b/drbg/hash_drbg_test.go @@ -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)]))