diff --git a/README.md b/README.md index fdb8add..76f6218 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Go语言商用密码软件,简称**GMSM**,一个安全、高性能、易于 * [SM3密码杂凑算法应用指南](./docs/sm3.md) * [SM4分组密码算法应用指南](./docs/sm4.md) * [SM9标识密码算法应用指南](./docs/sm9.md) +* [ZUC祖冲之序列密码算法应用指南](./docs/zuc.md) * [CFCA互操作性指南](./docs/cfca.md) * [PKCS7应用指南](./docs/pkcs7.md) * [PKCS12应用指南](./docs/pkcs12.md) diff --git a/docs/sm2.md b/docs/sm2.md index 9927e9f..d3e164b 100644 --- a/docs/sm2.md +++ b/docs/sm2.md @@ -5,7 +5,7 @@ * 《GB/T 32918.2-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第2部分:数字签名算法》 * 《GB/T 32918.3-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第3部分:密钥交换协议》 * 《GB/T 32918.4-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第4部分:公钥加密算法》 -* 《GB/T 32918.5-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第5部分:参数定义》 +* 《GB/T 32918.5-2017 信息安全技术 SM2椭圆曲线公钥密码算法 第5部分:参数定义》 * 《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》 * 《GB/T 33560-2017 信息安全技术 密码应用标识规范》 * 《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》(对应PKCS#7) diff --git a/docs/zuc.md b/docs/zuc.md new file mode 100644 index 0000000..074e6a6 --- /dev/null +++ b/docs/zuc.md @@ -0,0 +1,222 @@ +# 祖冲之序列密码算法应用指南 + +## 参考标准 +* 《GB/T 33133.1-2016 信息安全技术 祖冲之序列密码算法 第1部分:算法描述》 +* 《GB/T 33133.2-2021 信息安全技术 祖冲之序列密码算法 第2部分:保密性算法》 +* 《GB/T 33133.3-2021 信息安全技术 祖冲之序列密码算法 第2部分:完整性算法》 +* [《祖冲之算法:ZUC-256算法草案(中文)》](https://github.com/guanzhi/GM-Standards/blob/master/%E5%85%AC%E5%BC%80%E6%96%87%E6%A1%A3/%E7%A5%96%E5%86%B2%E4%B9%8B%E7%AE%97%E6%B3%95%EF%BC%9AZUC-256%E7%AE%97%E6%B3%95%E8%8D%89%E6%A1%88(%E4%B8%AD%E6%96%87).pdf) + +您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。 + +## 保密性算法 +保密性算法EEA实现了```cipher.Stream```接口,所以和其它流密码算法使用类似,只是创建方法不同而已。 + +| | ZUC-128 | ZUC-256 | +| :--- | :--- | :--- | +| Key字节数 | 16 | 32 | +| IV字节数 | 16 | 23 | + +```go +func ExampleNewCipher() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e676520746869732070617373") + plaintext := []byte("some plaintext") + + const ivSize = zuc.IVSize128 + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, ivSize+len(plaintext)) + iv := ciphertext[:ivSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + + stream, err := zuc.NewCipher(key, iv) + if err != nil { + panic(err) + } + stream.XORKeyStream(ciphertext[ivSize:], plaintext) + + // It's important to remember that ciphertexts must be authenticated + // (i.e. by using crypto/hmac) as well as being encrypted in order to + // be secure. + + // Stream cipher is the same for both encryption and decryption, so we can + // also decrypt that ciphertext with NewCTR. + + plaintext2 := make([]byte, len(plaintext)) + stream, err = zuc.NewCipher(key, iv) + if err != nil { + panic(err) + } + stream.XORKeyStream(plaintext2, ciphertext[ivSize:]) + + fmt.Printf("%s\n", plaintext2) + // Output: some plaintext +} + +func ExampleNewCipher_zuc256() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373") + plaintext := []byte("some plaintext") + + const ivSize = zuc.IVSize256 + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, ivSize+len(plaintext)) + iv := ciphertext[:ivSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + + stream, err := zuc.NewCipher(key, iv) + if err != nil { + panic(err) + } + stream.XORKeyStream(ciphertext[ivSize:], plaintext) + + // It's important to remember that ciphertexts must be authenticated + // (i.e. by using crypto/hmac) as well as being encrypted in order to + // be secure. + + // Stream cipher is the same for both encryption and decryption, so we can + // also decrypt that ciphertext with NewCTR. + + plaintext2 := make([]byte, len(plaintext)) + stream, err = zuc.NewCipher(key, iv) + if err != nil { + panic(err) + } + stream.XORKeyStream(plaintext2, ciphertext[ivSize:]) + + fmt.Printf("%s\n", plaintext2) + // Output: some plaintext +} +``` + +## 完整性算法 +完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。 + +| | ZUC-128 | ZUC-256 | +| :--- | :--- | :--- | +| Key字节数 | 16 | 32 | +| IV字节数 | 16 | 23 | +| MAC字节数 | 4 | 4/8/16 | + +```go +func ExampleNewHash() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e676520746869732070617373") + + // iv should be generated randomly + iv, _ := hex.DecodeString("6368616e676520746869732070617373") + + h, err := zuc.NewHash(key, iv) + if err != nil { + panic(err) + } + h.Write([]byte("hello world\n")) + fmt.Printf("%x", h.Sum(nil)) + // Output: c43cd26a +} + +func ExampleNewHash256_tagSize4() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373") + + // iv should be generated randomly + iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520") + + h, err := zuc.NewHash256(key, iv, 4) + if err != nil { + panic(err) + } + h.Write([]byte("hello world\n")) + fmt.Printf("%x", h.Sum(nil)) + // Output: b76f96ed +} + +func ExampleNewHash256_tagSize8() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373") + + // iv should be generated randomly + iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520") + + h, err := zuc.NewHash256(key, iv, 8) + if err != nil { + panic(err) + } + h.Write([]byte("hello world\n")) + fmt.Printf("%x", h.Sum(nil)) + // Output: f28aea6c9db3dc69 +} + +func ExampleNewHash256_tagSize16() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373") + + // iv should be generated randomly + iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520") + + h, err := zuc.NewHash256(key, iv, 16) + if err != nil { + panic(err) + } + h.Write([]byte("hello world\n")) + fmt.Printf("%x", h.Sum(nil)) + // Output: fd8d10ea65b6369cccc07d50b4657d84 +} +``` + +要支持位为单位的话,可以调用```Finish```方法。 +```go +func ExampleZUC128Mac_Finish() { + key := make([]byte, 16) + iv := make([]byte, 16) + h, err := zuc.NewHash(key, iv) + if err != nil { + panic(err) + } + fmt.Printf("%x", h.Finish([]byte{0}, 1)) + // Output: c8a9595e +} + +func ExampleZUC128Mac_Finish_mixed() { + key := []byte{ + 0xc9, 0xe6, 0xce, 0xc4, 0x60, 0x7c, 0x72, 0xdb, + 0x00, 0x0a, 0xef, 0xa8, 0x83, 0x85, 0xab, 0x0a, + } + + // iv should be generated randomly + iv, _ := hex.DecodeString("a94059da50000000294059da50008000") + + h, err := zuc.NewHash(key, iv) + if err != nil { + panic(err) + } + + in, _ := hex.DecodeString("983b41d47d780c9e1ad11d7eb70391b1de0b35da2dc62f83e7b78d6306ca0ea07e941b7be91348f9fcb170e2217fecd97f9f68adb16e5d7d21e569d280ed775cebde3f4093c53881") + h.Write(in) + fmt.Printf("%x", h.Finish([]byte{0}, 1)) + // Output: fae8ff0b +} +``` diff --git a/zuc/example_test.go b/zuc/example_test.go index c3e976c..f9b14b2 100644 --- a/zuc/example_test.go +++ b/zuc/example_test.go @@ -128,3 +128,72 @@ func ExampleNewHash256_tagSize4() { fmt.Printf("%x", h.Sum(nil)) // Output: b76f96ed } + +func ExampleNewHash256_tagSize8() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373") + + // iv should be generated randomly + iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520") + + h, err := zuc.NewHash256(key, iv, 8) + if err != nil { + panic(err) + } + h.Write([]byte("hello world\n")) + fmt.Printf("%x", h.Sum(nil)) + // Output: f28aea6c9db3dc69 +} + +func ExampleNewHash256_tagSize16() { + // Load your secret key from a safe place and reuse it across multiple + // NewCipher calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373") + + // iv should be generated randomly + iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520") + + h, err := zuc.NewHash256(key, iv, 16) + if err != nil { + panic(err) + } + h.Write([]byte("hello world\n")) + fmt.Printf("%x", h.Sum(nil)) + // Output: fd8d10ea65b6369cccc07d50b4657d84 +} + +func ExampleZUC128Mac_Finish() { + key := make([]byte, 16) + iv := make([]byte, 16) + h, err := zuc.NewHash(key, iv) + if err != nil { + panic(err) + } + fmt.Printf("%x", h.Finish([]byte{0}, 1)) + // Output: c8a9595e +} + +func ExampleZUC128Mac_Finish_mixed() { + key := []byte{ + 0xc9, 0xe6, 0xce, 0xc4, 0x60, 0x7c, 0x72, 0xdb, + 0x00, 0x0a, 0xef, 0xa8, 0x83, 0x85, 0xab, 0x0a, + } + + // iv should be generated randomly + iv, _ := hex.DecodeString("a94059da50000000294059da50008000") + + h, err := zuc.NewHash(key, iv) + if err != nil { + panic(err) + } + + in, _ := hex.DecodeString("983b41d47d780c9e1ad11d7eb70391b1de0b35da2dc62f83e7b78d6306ca0ea07e941b7be91348f9fcb170e2217fecd97f9f68adb16e5d7d21e569d280ed775cebde3f4093c53881") + h.Write(in) + fmt.Printf("%x", h.Finish([]byte{0}, 1)) + // Output: fae8ff0b +}