diff --git a/drbg/common.go b/drbg/common.go index 855146e..6cd0142 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 对内部状态进行清零处理 +// 内部状态组成为 {V,C, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +// 内部状态组成为 {V,Key, reseed_counter, last_reseed_time,reseed_interval_in_counter, reseed_interval_in_time} +func (cd *BaseDrbg) Destroy() { + setZero(cd.v) + cd.seedLength = 0 + for i := 0; i < 3; i++ { + // 使用原子操作防止编译器优化 + atomic.StoreUint64(&cd.reseedCounter, 0xFFFFFFFFFFFFFFFF) + atomic.StoreUint64(&cd.reseedCounter, 0x00) + atomic.StoreUint64(&cd.reseedIntervalInCounter, 0xFFFFFFFFFFFFFFFF) + atomic.StoreUint64(&cd.reseedIntervalInCounter, 0x00) + // 将 reseedIntervalInTime 设置内存屏障,防止编译器优化 + cd.reseedIntervalInTime = time.Duration(1<<63 - 1) + runtime.KeepAlive(&cd.reseedIntervalInTime) + cd.reseedIntervalInTime = time.Duration(0) + cd.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..98b03f7 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,8 @@ func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte { } return chainingValue } + +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..90faeb2 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[:16], entropyInput[16:24], 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..c69f233 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 根据 GM/T 0105-2021 B.2 对内部状态进行清零处理 +// 内部状态组成为 {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..6866b14 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 根据 GM/T 0105-2021 E.2 对内部状态进行清零处理 +// 内部状态组成为 {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..bd6613b 100644 --- a/drbg/hmac_drbg_test.go +++ b/drbg/hmac_drbg_test.go @@ -2,10 +2,12 @@ package drbg import ( "bytes" + "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/hex" + "github.com/emmansun/gmsm/sm3" "hash" "testing" ) @@ -802,3 +804,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") + } +}