From 62ee2eb20e5eb975eb1caafe4b6f6800cab09e19 Mon Sep 17 00:00:00 2001 From: Guanyu Quan Date: Mon, 29 Sep 2025 15:42:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86DRBG=E9=94=80?= =?UTF-8?q?=E6=AF=81=E5=86=85=E9=83=A8=E7=8A=B6=E6=80=81=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20(#378)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加了DRBG销毁内部状态的方法 * 统一前缀 * 修改随机数长度 * 分组和注释 * 错误函数描述 --- drbg/common.go | 47 ++++++++++++++++++++++++++++++++++++++++++ drbg/common_test.go | 22 +++++++++++++++++++- drbg/ctr_drbg.go | 9 +++++++- drbg/ctr_drbg_test.go | 17 +++++++++++++++ drbg/hash_drbg.go | 7 +++++++ drbg/hash_drbg_test.go | 17 +++++++++++++++ drbg/hmac_drbg.go | 7 +++++++ drbg/hmac_drbg_test.go | 19 +++++++++++++++++ 8 files changed, 143 insertions(+), 2 deletions(-) diff --git a/drbg/common.go b/drbg/common.go index 855146e..715ebf6 100644 --- a/drbg/common.go +++ b/drbg/common.go @@ -7,6 +7,8 @@ import ( "errors" "hash" "io" + "runtime" + "sync/atomic" "time" "github.com/emmansun/gmsm/sm3" @@ -226,6 +228,8 @@ type DRBG interface { Generate(b, additional []byte) error // MaxBytesPerRequest return max bytes per request MaxBytesPerRequest() int + // Destroy internal state + Destroy() } type BaseDrbg struct { @@ -258,6 +262,26 @@ func (hd *BaseDrbg) setSecurityLevel(securityLevel SecurityLevel) { } } +// Destroy 对 GM/T 0105-2021 B.2、E.2 对内部状态进行清零处理 +// HASH RNG 内部状态组成为 {V,C, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +// HMAC/对称加密 RNG 内部状态组成为 {V,Key, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +func (hd *BaseDrbg) Destroy() { + setZero(hd.v) + hd.seedLength = 0 + for i := 0; i < 3; i++ { + // 使用原子操作防止编译器优化 + atomic.StoreUint64(&hd.reseedCounter, 0xFFFFFFFFFFFFFFFF) + atomic.StoreUint64(&hd.reseedCounter, 0x00) + atomic.StoreUint64(&hd.reseedIntervalInCounter, 0xFFFFFFFFFFFFFFFF) + atomic.StoreUint64(&hd.reseedIntervalInCounter, 0x00) + // 将 reseedIntervalInTime 设置内存屏障,防止编译器优化 + hd.reseedIntervalInTime = time.Duration(1<<63 - 1) + runtime.KeepAlive(&hd.reseedIntervalInTime) + hd.reseedIntervalInTime = time.Duration(0) + hd.reseedTime = time.Now() + } +} + // Set security_strength to the lowest security strength greater than or equal to // requested_instantiation_security_strength from the set {112, 128, 192, 256}. func selectSecurityStrength(requested int) int { @@ -292,3 +316,26 @@ func addOne(data []byte, len int) { temp >>= 8 } } + +// setZero tries best to clear the sensitive data in memory by overwriting it with 0xFF and 0 for 3 times. +// - data: the byte slice to be cleared. +func setZero(data []byte) { + if data == nil { + return + } + for j := 0; j < 3; j++ { + // 先写入0xFF + for i := range data { + data[i] = 0xFF + } + // 内存屏障,确保写入0xFF完成 + runtime.KeepAlive(data) + + // 再写入0 + for i := range data { + data[i] = 0 + } + // 再次内存屏障,确保写入0完成 + runtime.KeepAlive(data) + } +} diff --git a/drbg/common_test.go b/drbg/common_test.go index 95cc43d..309e409 100644 --- a/drbg/common_test.go +++ b/drbg/common_test.go @@ -95,7 +95,6 @@ func TestNistHashDrbgPrng(t *testing.T) { } } - func TestNistHmacDrbgPrng(t *testing.T) { prng, err := NewNistHmacDrbgPrng(sha256.New, nil, 32, SECURITY_LEVEL_TEST, nil) if err != nil { @@ -121,3 +120,24 @@ func TestGMSecurityStrengthValidation(t *testing.T) { t.Fatalf("expected error here") } } + +func Test_setZero(t *testing.T) { + + cases := []struct { + name string + args []byte + }{ + {"nil", nil}, + {"empty", []byte{}}, + {"normal", []byte{1, 2, 3, 4, 5}}, + {"large", bytes.Repeat([]byte{1, 2, 3, 4, 5}, 100)}, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + setZero(tt.args) + if !bytes.Equal(tt.args, make([]byte, len(tt.args))) { + t.Errorf("setZero() = %v, want %v", tt.args, make([]byte, len(tt.args))) + } + }) + } +} diff --git a/drbg/ctr_drbg.go b/drbg/ctr_drbg.go index 101c342..ed23392 100644 --- a/drbg/ctr_drbg.go +++ b/drbg/ctr_drbg.go @@ -162,7 +162,7 @@ func (cd *CtrDrbg) update(seedMaterial []byte) { v := make([]byte, outlen) output := make([]byte, outlen) copy(v, cd.v) - for i := range (cd.seedLength+outlen-1)/outlen { + for i := range (cd.seedLength + outlen - 1) / outlen { // V = (V + 1) mod 2^outlen addOne(v, outlen) // output_block = Encrypt(Key, V) @@ -222,3 +222,10 @@ func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte { } return chainingValue } + +// Destroy destroys the internal state of DRBG instance +// 对称加密的RNG内部状态组成为 {V,Key, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +func (cd *CtrDrbg) Destroy() { + cd.BaseDrbg.Destroy() + setZero(cd.key) +} diff --git a/drbg/ctr_drbg_test.go b/drbg/ctr_drbg_test.go index 24762d8..6447891 100644 --- a/drbg/ctr_drbg_test.go +++ b/drbg/ctr_drbg_test.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/rand" "encoding/hex" "testing" @@ -303,3 +304,19 @@ func TestGmCtrDRBG_Validation(t *testing.T) { t.Fatalf("expected error here") } } + +func TestCtrDrbg_Destroy(t *testing.T) { + entropyInput := make([]byte, 64) + _, _ = rand.Reader.Read(entropyInput) + cd, err := NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:64], nil) + if err != nil { + t.Errorf("NewCtrDrbg failed: %v", err) + } + cd.Destroy() + if !bytes.Equal(cd.key, make([]byte, len(cd.key))) { + t.Errorf("Destroy failed: v not zeroed") + } + if !bytes.Equal(cd.v, make([]byte, len(cd.v))) { + t.Errorf("Destroy failed: key not zeroed") + } +} diff --git a/drbg/hash_drbg.go b/drbg/hash_drbg.go index fccf78a..5dea91e 100644 --- a/drbg/hash_drbg.go +++ b/drbg/hash_drbg.go @@ -222,3 +222,10 @@ func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte { } return k } + +// Destroy destroys the internal state of DRBG instance +// HASH 内部状态组成为 {V,C, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +func (hd *HashDrbg) Destroy() { + hd.BaseDrbg.Destroy() + setZero(hd.c) +} diff --git a/drbg/hash_drbg_test.go b/drbg/hash_drbg_test.go index 04735ec..63e7849 100644 --- a/drbg/hash_drbg_test.go +++ b/drbg/hash_drbg_test.go @@ -2,6 +2,7 @@ package drbg import ( "bytes" + "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" @@ -249,3 +250,19 @@ func TestGmHashDRBG_Validation(t *testing.T) { t.Fatalf("expected error here") } } + +func TestHashDrbg_Destroy(t *testing.T) { + entropyInput := make([]byte, 64) + _, _ = rand.Reader.Read(entropyInput) + hd, err := NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil) + if err != nil { + t.Errorf("NewHashDrbg failed: %v", err) + } + hd.Destroy() + if !bytes.Equal(hd.c, make([]byte, len(hd.c))) { + t.Errorf("Destroy failed: v not zeroed") + } + if !bytes.Equal(hd.v, make([]byte, len(hd.v))) { + t.Errorf("Destroy failed: key not zeroed") + } +} diff --git a/drbg/hmac_drbg.go b/drbg/hmac_drbg.go index 566cf1c..75b0640 100644 --- a/drbg/hmac_drbg.go +++ b/drbg/hmac_drbg.go @@ -153,3 +153,10 @@ func (hd *HmacDrbg) update(byteSlices ...[]byte) error { hd.v = md.Sum(hd.v[:0]) return nil } + +// Destroy destroys the internal state of DRBG instance +// HMAC的RNG内部状态组成为 {V,Key, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +func (hd *HmacDrbg) Destroy() { + hd.BaseDrbg.Destroy() + setZero(hd.key) +} diff --git a/drbg/hmac_drbg_test.go b/drbg/hmac_drbg_test.go index 5435991..477709f 100644 --- a/drbg/hmac_drbg_test.go +++ b/drbg/hmac_drbg_test.go @@ -2,12 +2,15 @@ package drbg import ( "bytes" + "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/hex" "hash" "testing" + + "github.com/emmansun/gmsm/sm3" ) var hmactests = []struct { @@ -802,3 +805,19 @@ func TestHmacDRBG(t *testing.T) { } } } + +func TestHmacDrbg_Destroy(t *testing.T) { + entropyInput := make([]byte, 64) + _, _ = rand.Reader.Read(entropyInput) + hd, err := NewHmacDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil) + if err != nil { + t.Errorf("NewHmacDrbg failed: %v", err) + } + hd.Destroy() + if !bytes.Equal(hd.key, make([]byte, len(hd.key))) { + t.Errorf("Destroy failed: v not zeroed") + } + if !bytes.Equal(hd.v, make([]byte, len(hd.v))) { + t.Errorf("Destroy failed: key not zeroed") + } +}