From 643df78c074af44e3c60aaaad3236488686e89e3 Mon Sep 17 00:00:00 2001 From: Sun Yimin Date: Thu, 20 Oct 2022 11:30:20 +0800 Subject: [PATCH] implement ctr drbg --- drbg/common.go | 61 +++++++++ drbg/ctr_drbg.go | 201 +++++++++++++++++++++++++++++ drbg/ctr_drbg_test.go | 285 +++++++++++++++++++++++++++++++++++++++++ drbg/hash_drbg.go | 74 +++-------- drbg/hash_drbg_test.go | 21 +++ 5 files changed, 585 insertions(+), 57 deletions(-) create mode 100644 drbg/common.go create mode 100644 drbg/ctr_drbg.go create mode 100644 drbg/ctr_drbg_test.go diff --git a/drbg/common.go b/drbg/common.go new file mode 100644 index 0000000..cbfa6d8 --- /dev/null +++ b/drbg/common.go @@ -0,0 +1,61 @@ +package drbg + +import ( + "time" +) + +const DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10 +const DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20 +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 + +type SecurityLevel byte + +const ( + SECURITY_LEVEL_ONE SecurityLevel = 0x01 + SECURITY_LEVEL_TWO SecurityLevel = 0x02 +) + +// DRBG interface for both hash and ctr drbg implementations +type DRBG interface { + // check internal state, return if reseed required + NeedReseed() bool + // reseed process + Reseed(entropy, additional []byte) error + // generate requrested bytes to b + Generate(b, additional []byte) error +} + +type BaseDrbg struct { + v []byte + seedLength int + reseedTime time.Time + reseedIntervalInTime time.Duration + reseedCounter uint64 + reseedIntervalInCounter uint64 + securityLevel SecurityLevel + gm bool +} + +func (hd *BaseDrbg) NeedReseed() bool { + return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime) +} + +func add(left, right []byte, len int) { + var temp uint16 = 0 + for i := len - 1; i >= 0; i-- { + temp += uint16(left[i]) + uint16(right[i]) + right[i] = byte(temp & 0xff) + temp >>= 8 + } +} + +func addOne(data []byte, len int) { + var temp uint16 = 1 + for i := len - 1; i >= 0; i-- { + temp += uint16(data[i]) + data[i] = byte(temp & 0xff) + temp >>= 8 + } +} diff --git a/drbg/ctr_drbg.go b/drbg/ctr_drbg.go new file mode 100644 index 0000000..5d51014 --- /dev/null +++ b/drbg/ctr_drbg.go @@ -0,0 +1,201 @@ +package drbg + +import ( + "crypto/cipher" + "encoding/binary" + "errors" + "time" + + "github.com/emmansun/gmsm/internal/subtle" + "github.com/emmansun/gmsm/sm4" +) + +type CtrDrbg struct { + BaseDrbg + cipherProvider func(key []byte) (cipher.Block, error) + key []byte + keyLen int +} + +// NewCtrDrbg create one CTR DRBG instance +func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*CtrDrbg, error) { + hd := &CtrDrbg{} + + hd.gm = gm + hd.securityLevel = securityLevel + hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 + hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL1 + if hd.securityLevel == SECURITY_LEVEL_TWO { + hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 + hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL2 + } + + // here for the min length, we just check <=0 now + if len(entropy) <= 0 || 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 { + return nil, errors.New("invalid nonce length") + } + + if len(personalization) >= MAX_BYTES { + return nil, errors.New("personalization is too long") + } + + hd.cipherProvider = cipherProvider + hd.keyLen = keyLen + temp := make([]byte, hd.keyLen) + block, err := cipherProvider(temp) + if err != nil { + return nil, err + } + hd.seedLength = block.BlockSize() + keyLen + hd.v = make([]byte, block.BlockSize()) + hd.key = make([]byte, hd.keyLen) + + 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) + hd.reseedCounter = 1 + hd.reseedTime = time.Now() + return hd, nil +} + +// NewNISTCtrDrbg create one CTR DRBG implementation which follows NIST standard +func NewNISTCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) { + return NewCtrDrbg(cipherProvider, keyLen, securityLevel, false, entropy, nonce, personalization) +} + +// NewGMCtrDrbg create one CTR DRBG implementation which follows GM/T 0105-2021 standard +func NewGMCtrDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) { + return NewCtrDrbg(sm4.NewCipher, 16, securityLevel, true, 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 { + return errors.New("invalid entropy length") + } + + if len(additional) >= MAX_BYTES { + return errors.New("additional input too long") + } + + var seedMaterial []byte + if len(additional) == 0 { + seedMaterial = entropy + } else { + seedMaterial = make([]byte, len(entropy)+len(additional)) + copy(seedMaterial, entropy) + copy(seedMaterial[len(entropy):], additional) + } + seed := hd.derive(seedMaterial, hd.seedLength) + hd.update(seed) + hd.reseedCounter = 1 + hd.reseedTime = time.Now() + return nil +} + +func (hd *CtrDrbg) newBlockCipher(key []byte) cipher.Block { + block, err := hd.cipherProvider(key) + if err != nil { + panic(err) + } + return block +} + +// Generate CTR DRBG generate process. +func (hd *CtrDrbg) Generate(b, additional []byte) error { + if hd.NeedReseed() { + return errors.New("reseed reuqired") + } + outlen := len(hd.v) + if (hd.gm && len(b) > outlen) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) { + return errors.New("too many bytes requested") + } + + if len(additional) > 0 { + additional = hd.derive(additional, hd.seedLength) + hd.update(additional) + } + + block := hd.newBlockCipher(hd.key) + temp := make([]byte, outlen) + + m := len(b) + limit := uint64(m+outlen-1) / uint64(outlen) + for i := 0; i < int(limit); i++ { + addOne(hd.v, outlen) + block.Encrypt(temp, hd.v) + copy(b[i*outlen:], temp) + } + hd.update(additional) + hd.reseedCounter++ + return nil +} + +func (cd *CtrDrbg) update(seedMaterial []byte) { + temp := make([]byte, cd.seedLength) + block := cd.newBlockCipher(cd.key) + + outlen := block.BlockSize() + v := make([]byte, outlen) + output := make([]byte, outlen) + copy(v, cd.v) + for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ { + addOne(v, outlen) + block.Encrypt(output, v) + copy(temp[i*outlen:], output) + } + subtle.XORBytes(temp, temp, seedMaterial) + copy(cd.key, temp) + copy(cd.v, temp[cd.keyLen:]) +} + +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) + + binary.BigEndian.PutUint32(S[outlen:], uint32(len(seedMaterial))) + binary.BigEndian.PutUint32(S[outlen+4:], uint32(returnBytes)) + copy(S[outlen+8:], seedMaterial) + S[outlen+8+len(seedMaterial)] = 0x80 + + key := make([]byte, cd.keyLen) + for i := 0; i < cd.keyLen; i++ { + key[i] = byte(i) + } + blocks := (cd.seedLength + outlen - 1) / outlen + temp := make([]byte, blocks*outlen) + block := cd.newBlockCipher(key) + + for i := 0; i < blocks; i++ { + binary.BigEndian.PutUint32(S, uint32(i)) + copy(temp[i*outlen:], cd.bcc(block, S)) + } + + key = temp[:cd.keyLen] + X := temp[cd.keyLen:cd.seedLength] + temp = make([]byte, returnBytes) + block = cd.newBlockCipher(key) + for i := 0; i < (returnBytes+outlen-1)/outlen; i++ { + block.Encrypt(X, X) + copy(temp[i*outlen:], X) + } + return temp +} + +func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte { + chainingValue := make([]byte, block.BlockSize()) + for i := 0; i < len(data)/block.BlockSize(); i++ { + subtle.XORBytes(chainingValue, chainingValue, data[i*block.BlockSize():]) + block.Encrypt(chainingValue, chainingValue) + } + return chainingValue +} diff --git a/drbg/ctr_drbg_test.go b/drbg/ctr_drbg_test.go new file mode 100644 index 0000000..2b53290 --- /dev/null +++ b/drbg/ctr_drbg_test.go @@ -0,0 +1,285 @@ +package drbg + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/hex" + "testing" + + "github.com/emmansun/gmsm/sm4" +) + +var ctrtests = []struct { + gm bool + cipherProvider func(key []byte) (cipher.Block, error) + keyLen int + entropyInput string + nonce string + personalizationString string + v0 string + key0 string + entropyInputReseed string + additionalInputReseed string + v1 string + key1 string + additionalInput1 string + v2 string + key2 string + additionalInput2 string + returnbits1 string + v3 string + key3 string +}{ + { // AES-128, without additional input + false, + aes.NewCipher, + 16, + "0f65da13dca407999d4773c2b4a11d85", + "5209e5b4ed82a234", + "", + "80941680713df715056fb2a3d2e998b2", + "0c42ea6804303954deb197a07e6dbdd2", + "1dea0a12c52bf64339dd291c80d8ca89", + "", + "f2bacbb233252fba35fb0582f9286179", // v1 + "32fbfd0109f364ed21ef21a6e5c763e7", //key1 + "", + "99003d630bba500fe17c37f8c7331bf6", // v2 + "757c8eb766f9aaa4650d6500b58624a3", //key2 + "", + "2859cc468a76b08661ffd23b28547ffd0997ad526a0f51261b99ed3a37bd407bf418dbe6c6c3e26ed0ddefcb7474d899bd99f3655427519fc5b4057bcaf306d4", + "5907ab447a88e5106753507cc97e0fd5", + "e421ff2445e04992faf36cf9a5eaf1f9", + }, + { // AES-128, without additional input + false, + aes.NewCipher, + 16, + "c9b8d7eb0afa5889e7f9b78a50ed453c", + "3058ba347ecd11b1", + "", + "b4e0180e3af0d99592249db33a29cc4e", + "1621bebef7e9215078459ecc74baffbc", + "643686b86266d9111f29eb389e1184b4", + "", + "7574911eeb85d56d385f0c8c99965c4a", // v1 + "a4c515266cb5986825a503b39d5f398c", //key1 + "", + "abca3ea049e405d3826f43e54e08c8f7", // v2 + "edcdf23f60d3988a4d235798aa0d33a2", //key2 + "", + "0a8ccadc1c5cbd20b8ce32f942505e654b91a4e9410e0ea627c961d632d3be71d6a7dfd64b8f70d28ff91869b92ced908b454936b6d18fcddd7fb77216ccc404", + "1d57b4e09fd920d91877a0737559ee29", + "e83e07722d26779d0b76a52a629b211b", + }, + { // AES-128, with additional input + false, + aes.NewCipher, + 16, + "285da6cf762552634636bfee3400b156", + "8f8bada74820cb43", + "", + "ad2af7e4c84337cfc3116d59f02c54a8", // v0 + "c92780982442d348cc7363dfc96a999d", // key0 + "b4699b33354a83bfed115f770f32db0b", // EntropyInputReseed + "38bfec9a10e6e40c106841dae48dc3b8", // AdditionalInputReseed + "923f37427a8e10bf945249a5b790769a", // v1 + "57004c8a776f5c702e83ff56acc32dcc", // key1 + "629ead5bacfac8235711ffeb22f57558", // AdditionalInput1 + "7ade619ed91092987d8a1d244605f85f", // v2 + "3b5f92f511c10fef2f640de2cd8c9049", // key2 + "dd8a02ee668ca3e03949b38cb6e6b4df", // AdditionalInput2 + "e555aa4432bde04dcf0f0b03ead187b31df06653d444234b5c1bfc11b224285f2fb2b6cdd5a9ae6f13d99bd02c3c9fe9c3c1be46a600f5f757ab4574af893501", + "f5dac2375e820f797c6f1258147d8ea7", // v3 + "6bc01c1518fe9f9dfbbb08d97c34db1e", // key3 + }, + { // AES-192, without additional input + false, + aes.NewCipher, + 24, + "b11d8b104a7ced9b9f37e5d92ad3dfcbb817552b1ae88f6a", + "017510f270c66586a51313eadc32b07e", + "", + "9e5767ab537fe663c71e4054ba618c8d", // v0 + "b9b3d73bc0c784a7d78db344109707c73abbff7dc2dfa864", // key0 + "6d14cfb36f30c9c1a1ba0e0a32c2f99d1b47f219a3a8ac14", // EntropyInputReseed + "", // AdditionalInputReseed + "c8563c5a4adc3b579f79f898c4b69854", // v1 + "3e18d4984d454e5f986e49bfa7a569dab3667ece8130cba1", // key1 + "", // AdditionalInput1 + "087a3112e191f60619acae2a556f333b", // v2 + "b42a24cbb9e8c014bb65350afa28a67b273a41e599bde5b8", // key2 + "", // AdditionalInput2 + "53fbba563ae014ebc080767aab8452a9f36ce40bbf68f1a12dc0a6388c870c8dfa4250526cbc8c983fee6449903c6bd7c2c02e327680a66b464267edbc4e6797", + "84f344f8277841e920464ca475b10276", // v3 + "1f5e987ac2259b7072867e4ae59167094d0162111062f6f8", // key3 + }, + { // AES-192, with additional input + false, + aes.NewCipher, + 24, + "3a09c9cc5e01f152ea2ed3021d49b4d6386aa6f04521ebde", + "490bd4ee628cf9615035543e70fce4e2", + "", + "59a45ccbc3864f79b896c30d4a231d46", // v0 + "a4283dc9450ac97bf22c387082e3816728243473cedaa2af", // key0 + "df06e5668d41a6fa7660aef477eff7a0ffc0542c1cd406d5", // EntropyInputReseed + "59b8c26626aab69e462752722f19450d12e2c0e959882d4d06ef4177e396855d", // AdditionalInputReseed + "5857d49a1552923931926dca1682fbc2", // v1 + "9c4d7784fe341619e21f2535d404866df3b75e9a7940d471", // key1 + "28e57a9128e479985cce391e98127fd126f37ad0f317fd5f97b8c18e762f360b", // AdditionalInput1 + "bb8ed7bcbe1203be861b8e6570fe116b", // v2 + "6a8fddde995255f89ea3c9454cc481045ff0e16ce5a34693", // key2 + "d488672b52e867816178369f542190685bbe8672720c1943d8a4378cc9b9dd0c", // AdditionalInput2 + "5c233e2850e4981bab0f6513a76ca2c9f9f97b89b7fedd3d9aaffecf305d89fd5306cf24715895ad9ba7dac8c389fd87f95b4973003150871fa281e962f270cb", + "1cf82a0638c421bb43401943498d0f88", // v3 + "5dec9ad1f5f3d0e7bb59ae581097a3f616e443e4f5bd804a", // key3 + }, + { // AES-256, without additional input + false, + aes.NewCipher, + 32, + "2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5", + "0bf814b411f65ec4866be1abb59d3c32", + "", + "446ce986bd722ad1a514ebb7d274ec99", // v0 + "d64160c3e965f377caef625c7eb21dd37728bcf84bfc23b92e267611feaffda8", // key0 + "93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207", // EntropyInputReseed + "", // AdditionalInputReseed + "0b8e38a54036f1ba80a2880d4f17bb09", // v1 + "50d9feb33fc77303b83232b7deded04f1bfa4afaa937712f88458d6b64c046c5", // key1 + "", // AdditionalInput1 + "84b0a849c5459e27fe7f8c5db26fa13d", // v2 + "a2203a6f082ecdc0cd38f0b3b19f1a8cd6a5f110a13bb488c1e70f9f95a93024", // key2 + "", // AdditionalInput2 + "322dd28670e75c0ea638f3cb68d6a9d6e50ddfd052b772a7b1d78263a7b8978b6740c2b65a9550c3a76325866fa97e16d74006bc96f26249b9f0a90d076f08e5", + "de67dd5f9a431fc46dd1825cd1a2bff3", // v3 + "de721178a341a85eb54a2f7e2b3cd4bcc201417e739eb183fa958f9af8535b2c", // key3 + }, + { // AES-256, with additional input + false, + aes.NewCipher, + 32, + "6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e", + "401e3f87762fa8a14ab232ccb8480a2f", + "", + "ee534dcfd9d2be3a3f9c65a6c5f599b0", // v0 + "6d9aa2e029466438d3e4c22530bd071dbe57b549b87370957b28da8ae083f8d6", // key0 + "350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed + "7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed + "433725f6c4b8c662c3b2db4b75f38d86", // v1 + "b5953178a900b2fcf052b5cbc1d882ea944da2965e84fef59c4919bb4d5c892d", // key1 + "67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1 + "2c342b2ab12bd3484e4660b8dd5f85eb", // v2 + "b2b9e9f1ffcfd84c050445f93dfad90d6ca240494bbed5d44a0deb38fbaeb751", // key2 + "648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2 + "2d819fb9fee38bfc3f15a07ef0e183ff36db5d3184cea1d24e796ba103687415abe6d9f2c59a11931439a3d14f45fc3f4345f331a0675a3477eaf7cd89107e37", + "a9729f842063b9464e74018c0ab30df3", // v3 + "770600434fe0af64e045f5530e2b9732da9e3b4c3af342994a4f1f7ee5c4144e", // key3 + }, + { // SM4-128, without additional input + true, + sm4.NewCipher, + 16, + "0f65da13dca407999d4773c2b4a11d85", + "5209e5b4ed82a234", + "", + "3dee0b770815026b88a86a1637c4c9f6", + "40ef052e0dffc441fd644f6dce7430c0", + "1dea0a12c52bf64339dd291c80d8ca89", + "", + "1c5c3a1369b7ab6ea6f5631b0a8e4f2d", // v1 + "ac5783e385d6d4c6a2c5a184e7cebecb", //key1 + "", + "22b16dfe04896ef1ddf69f12e5d9a1dd", // v2 + "3f7b6539fc274b97565b2a1b26d021e0", //key2 + "", + "58129d515c9bb6d32e1ab1206fe6c618", + "69bfff2ab5f3dbcc5eaca7eb5a3cf8e4", + "c2b4a693df1f768e7d45d0926a40f527", + }, + { // SM4-128, with additional input + false, + sm4.NewCipher, + 16, + "285da6cf762552634636bfee3400b156", + "8f8bada74820cb43", + "", + "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 + }, +} + +func TestCtrDRBG(t *testing.T) { + for i, test := range ctrtests { + entropyInput, _ := hex.DecodeString(test.entropyInput) + nonce, _ := hex.DecodeString(test.nonce) + personalizationString, _ := hex.DecodeString(test.personalizationString) + v0, _ := hex.DecodeString(test.v0) + 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) + } + if !bytes.Equal(hd.v[:len(v0)], v0) { + t.Errorf("case %v, not same v0 %s", i+1, hex.EncodeToString(hd.v)) + } + if !bytes.Equal(hd.key[:len(key0)], key0) { + t.Errorf("case %v, not same key0 %s", i+1, hex.EncodeToString(hd.key)) + } + // Reseed + entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed) + additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed) + v1, _ := hex.DecodeString(test.v1) + key1, _ := hex.DecodeString(test.key1) + err = hd.Reseed(entropyInputReseed, additionalInputReseed) + if err != nil { + t.Error(err) + } + if !bytes.Equal(hd.v, v1) { + t.Errorf("case %v, not same v1 %s", i+1, hex.EncodeToString(hd.v)) + } + if !bytes.Equal(hd.key, key1) { + t.Errorf("case %v, not same key1 %s", i+1, hex.EncodeToString(hd.key)) + } + // Generate 1 + returnbits1, _ := hex.DecodeString(test.returnbits1) + v2, _ := hex.DecodeString(test.v2) + key2, _ := hex.DecodeString(test.key2) + output := make([]byte, len(returnbits1)) + additionalInput1, _ := hex.DecodeString(test.additionalInput1) + hd.Generate(output, additionalInput1) + if !bytes.Equal(hd.v, v2) { + t.Errorf("case %v, not same v2 %s", i+1, hex.EncodeToString(hd.v)) + } + if !bytes.Equal(hd.key, key2) { + t.Errorf("case %v, not same key2 %s", i+1, hex.EncodeToString(hd.key)) + } + // Generate 2 + v3, _ := hex.DecodeString(test.v3) + key3, _ := hex.DecodeString(test.key3) + additionalInput2, _ := hex.DecodeString(test.additionalInput2) + hd.Generate(output, additionalInput2) + if !bytes.Equal(hd.v[:len(v0)], v3) { + t.Errorf("case %v, not same v3 %s", i+1, hex.EncodeToString(hd.v)) + } + if !bytes.Equal(hd.key, key3) { + t.Errorf("case %v, not same key3 %s", i+1, hex.EncodeToString(hd.key)) + } + if !bytes.Equal(returnbits1, output) { + t.Errorf("case %v, not expected return bits %s", i+1, hex.EncodeToString(output)) + } + } +} diff --git a/drbg/hash_drbg.go b/drbg/hash_drbg.go index 71f9d64..17be8fb 100644 --- a/drbg/hash_drbg.go +++ b/drbg/hash_drbg.go @@ -1,40 +1,22 @@ package drbg import ( - "crypto/sha256" "encoding/binary" "errors" "hash" "time" + + "github.com/emmansun/gmsm/sm3" ) const HASH_DRBG_SEED_SIZE = 55 const HASH_DRBG_MAX_SEED_SIZE = 111 -const HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10 -const HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20 -const HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL2 = time.Duration(60) * time.Second -const HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL1 = time.Duration(600) * time.Second -const MAX_BYTES = 1 << 27 const MAX_BYTES_PER_GENERATE = 1 << 11 -type SecurityLevel byte - -const ( - SECURITY_LEVEL_ONE SecurityLevel = 0x01 - SECURITY_LEVEL_TWO SecurityLevel = 0x02 -) - type HashDrbg struct { - md hash.Hash - v []byte - c []byte - seedLength int - reseedTime time.Time - reseedIntervalInTime time.Duration - reseedCounter uint64 - reseedIntervalInCounter uint64 - securityLevel SecurityLevel - gm bool + BaseDrbg + md hash.Hash + c []byte } // NewHashDrbg create one hash DRBG instance @@ -43,11 +25,11 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no hd.gm = gm hd.securityLevel = securityLevel - hd.reseedIntervalInCounter = HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 - hd.reseedIntervalInTime = HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL1 + hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 + hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL1 if hd.securityLevel == SECURITY_LEVEL_TWO { - hd.reseedIntervalInCounter = HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 - hd.reseedIntervalInTime = HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL2 + hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 + hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL2 } // here for the min length, we just check <=0 now @@ -65,7 +47,7 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no } hd.md = md - if md.Size() <= sha256.Size { + if md.Size() <= sm3.Size { hd.v = make([]byte, HASH_DRBG_SEED_SIZE) hd.c = make([]byte, HASH_DRBG_SEED_SIZE) hd.seedLength = HASH_DRBG_SEED_SIZE @@ -91,14 +73,14 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no return hd, nil } -// NewNISTHashDrbg return hash DRBG implementation which follow NIST standard +// NewNISTHashDrbg return hash DRBG implementation which follows NIST standard func NewNISTHashDrbg(md hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) { return NewHashDrbg(md, securityLevel, false, entropy, nonce, personalization) } -// NewGMHashDrbg return hash DRBG implementation which follow GM/T 0105-2021 standard -func NewGMHashDrbg(md hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) { - return NewHashDrbg(md, securityLevel, true, entropy, nonce, personalization) +// NewGMHashDrbg return hash DRBG implementation which follows GM/T 0105-2021 standard +func NewGMHashDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) { + return NewHashDrbg(sm3.New(), securityLevel, true, entropy, nonce, personalization) } // Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST. @@ -135,19 +117,6 @@ func (hd *HashDrbg) Reseed(entropy, additional []byte) error { return nil } -func (hd *HashDrbg) NeedReseed() bool { - return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime) -} - -func add(left, right []byte, len int) { - var temp uint16 = 0 - for i := len - 1; i >= 0; i-- { - temp += uint16(left[i]) + uint16(right[i]) - right[i] = byte(temp & 0xff) - temp >>= 8 - } -} - func (hd *HashDrbg) addW(w []byte) { t := make([]byte, hd.seedLength) copy(t[hd.seedLength-len(w):], w) @@ -171,15 +140,6 @@ func (hd *HashDrbg) addReseedCounter() { add(t, hd.v, hd.seedLength) } -func (hd *HashDrbg) addOne(data []byte) { - var temp uint16 = 1 - for i := hd.seedLength - 1; i >= 0; i-- { - temp += uint16(data[i]) - data[i] = byte(temp & 0xff) - temp >>= 8 - } -} - // Generate hash DRBG generate 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 { @@ -210,7 +170,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error { for i := 0; i < int(limit); i++ { md.Write(data) copy(b[i*md.Size():], md.Sum(nil)) - hd.addOne(data) + addOne(data, hd.seedLength) md.Reset() } } @@ -222,7 +182,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error { return nil } -func (hd *HashDrbg) derive(seedMaternial []byte, len int) []byte { +func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte { md := hd.md limit := uint64(len+md.Size()-1) / uint64(md.Size()) var requireBytes [4]byte @@ -232,7 +192,7 @@ func (hd *HashDrbg) derive(seedMaternial []byte, len int) []byte { for i := 0; i < int(limit); i++ { md.Write([]byte{ct}) md.Write(requireBytes[:]) - md.Write(seedMaternial) + md.Write(seedMaterial) copy(k[i*md.Size():], md.Sum(nil)) ct++ md.Reset() diff --git a/drbg/hash_drbg_test.go b/drbg/hash_drbg_test.go index fcbbdf0..dfad8a8 100644 --- a/drbg/hash_drbg_test.go +++ b/drbg/hash_drbg_test.go @@ -156,6 +156,24 @@ var tests = []struct { "00d98d35a2fab8df23e9e1fb9aad143d62c0759eb79e15c37e8f2bc5064e68da", "299d084f049dd9b7f62ee712b5b2c1c602f078980f4d9816d8f2baf38765be984b6c493497af30f68a56072404f27e45af419d04eb9e35", }, + { + true, + sm3.New(), + "9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c", + "a20765538e8db31295747ec922c13a69", + "", + "997cd31a7032c8643ca56de1d34ff4f930b13192e17c8947bcaf9b9d010cf79805511255c7ea18b41cde77e491ca943861ec29780f3f36", // v0 + "4c5b167d27fa9d40cbc45d0d9f3c52504a1cb5aa2f37a3fa812037bd1e458412ecff0641dd5cb2785d0f8044151b42842777211547b457", // c0 + "96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc", + "6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d", + "e3d804eda66df62f425c41047c5812fca471c8236395c92d4bd834c2e52d606be6ad3da8973df16e8567bcb16e45f2842ace91bf6dfeb3", // v1 + "1a1b2fffca26953626e0fa3afd377e14e63c5e81275b39f1436b707efb2c3059e6ced8fdb238a45bf05aae9f2417dbf5c4f89d3772f324", // c1 + "d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6", + "fdf334ed70948b65693d3b3f798f91118aae26a48af1045be386984b75695902870479c3c53593d332d195ee7f1bed45fc5069cc4f9948", // v2 + "5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b", + "48709db5509a03d6131775fbbfe74fe52611e760d22fde61e274a295f4354d67", + "180e64ed3abb209b901e357a76c70f2670ea8525b24c401d146b70b178e33d8fd10f7680c50bbe1f9773ba664dd14cd25d329c380bf399", // v3 + }, } func TestHashDRBG(t *testing.T) { @@ -175,6 +193,7 @@ func TestHashDRBG(t *testing.T) { if !bytes.Equal(hd.c[:len(c0)], c0) { t.Errorf("not same c0 %s", hex.EncodeToString(hd.c[:len(c0)])) } + // Reseed entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed) additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed) v1, _ := hex.DecodeString(test.v1) @@ -189,6 +208,7 @@ func TestHashDRBG(t *testing.T) { if !bytes.Equal(hd.c[:len(c0)], c1) { t.Errorf("not same c1 %s", hex.EncodeToString(hd.c[:len(c0)])) } + // Generate 1 returnbits1, _ := hex.DecodeString(test.returnbits1) v2, _ := hex.DecodeString(test.v2) output := make([]byte, len(returnbits1)) @@ -197,6 +217,7 @@ func TestHashDRBG(t *testing.T) { if !bytes.Equal(hd.v[:len(v0)], v2) { t.Errorf("not same v2 %s", hex.EncodeToString(hd.v[:len(v0)])) } + // Generate 2 v3, _ := hex.DecodeString(test.v3) additionalInput2, _ := hex.DecodeString(test.additionalInput2) hd.Generate(output, additionalInput2)