add example test for sm2 sm4

This commit is contained in:
Sun Yimin 2023-02-01 11:17:23 +08:00 committed by GitHub
parent 9c8b345630
commit e3db7656a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 312 additions and 94 deletions

170
sm2/example_test.go Normal file
View File

@ -0,0 +1,170 @@
package sm2_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"log"
"math/big"
"os"
"github.com/emmansun/gmsm/sm2"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
)
// This example method is just for reference, it's NOT a standard method for key transmission.
// In general, private key will be encoded/formatted with PKCS8, public key will be encoded/formatted with a SubjectPublicKeyInfo structure
// (see RFC 5280, Section 4.1).
func Example_createKeysFromRawValue() {
key, _ := sm2.GenerateKey(rand.Reader)
d := new(big.Int).SetBytes(key.D.Bytes()) // here we do NOT check if the d is in (0, N) or not
// Create private key from *big.Int
keyCopy := new(sm2.PrivateKey)
keyCopy.Curve = sm2.P256()
keyCopy.D = d
keyCopy.PublicKey.X, keyCopy.PublicKey.Y = keyCopy.ScalarBaseMult(keyCopy.D.Bytes())
if !key.Equal(keyCopy) {
log.Fatalf("private key and copy should be equal")
}
pointBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
// Create public key from point (uncompressed)
publicKeyCopy := new(ecdsa.PublicKey)
publicKeyCopy.Curve = sm2.P256()
publicKeyCopy.X, publicKeyCopy.Y = elliptic.Unmarshal(publicKeyCopy.Curve, pointBytes)
if !key.PublicKey.Equal(publicKeyCopy) {
log.Fatalf("public key and copy should be equal")
}
}
// This method provide a sample to handle ASN1 ciphertext ends with extra bytes.
func Example_parseCipherASN1EndsWithInvalidBytes() {
// a sample method to get frist ASN1 SEQUENCE data
getFirstASN1Sequence := func(ciphertext []byte) ([]byte, []byte, error) {
input := cryptobyte.String(ciphertext)
var inner cryptobyte.String
if !input.ReadASN1(&inner, asn1.SEQUENCE) {
return nil, nil, errors.New("there are no sequence tag")
}
if len(input) == 0 {
return ciphertext, nil, nil
}
return ciphertext[:len(ciphertext)-len(input)], input, nil
}
ciphertext, _ := hex.DecodeString("3081980220298ED52AE2A0EBA8B7567D54DF41C5F9B310EDFA4A8E15ECCB44EDA94F9F1FC20220116BE33B0833C95D8E5FF9483CD2D7EFF7033C92FE5DEAB6197D809FF1EEE05F042097A90979A6FCEBDE883C2E07E9C286818E694EDE37C3CDAA70E4CD481BE883E00430D62160BB179CB20CE3B5ECA0F5A535BEB6E221566C78FEA92105F71BD37F3F850AD2F86F2D1E35F15E9356557DAC026A")
_, rest, err := getFirstASN1Sequence(ciphertext)
if err != nil || len(rest) != 0 {
log.Fatalf("can't get a complete ASN1 sequence")
}
ciphertext, _ = hex.DecodeString("3081980220298ED52AE2A0EBA8B7567D54DF41C5F9B310EDFA4A8E15ECCB44EDA94F9F1FC20220116BE33B0833C95D8E5FF9483CD2D7EFF7033C92FE5DEAB6197D809FF1EEE05F042097A90979A6FCEBDE883C2E07E9C286818E694EDE37C3CDAA70E4CD481BE883E00430D62160BB179CB20CE3B5ECA0F5A535BEB6E221566C78FEA92105F71BD37F3F850AD2F86F2D1E35F15E9356557DAC026A0000")
seq, rest, err := getFirstASN1Sequence(ciphertext)
if err != nil || len(rest) != 2 {
log.Fatalf("can't get a complete ASN1 sequence")
}
var (
x1, y1 = &big.Int{}, &big.Int{}
c2, c3 []byte
inner cryptobyte.String
)
input := cryptobyte.String(seq)
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
!input.Empty() ||
!inner.ReadASN1Integer(x1) ||
!inner.ReadASN1Integer(y1) ||
!inner.ReadASN1Bytes(&c3, asn1.OCTET_STRING) ||
!inner.ReadASN1Bytes(&c2, asn1.OCTET_STRING) ||
!inner.Empty() {
log.Fatalf("invalid cipher text")
}
}
// This is a reference method to force SM2 standard with SDK [crypto.Signer].
func ExamplePrivateKey_Sign_forceSM2() {
toSign := []byte("ShangMi SM2 Sign Standard")
// real private key should be from secret storage
privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
d := new(big.Int).SetBytes(privKey)
testkey := new(sm2.PrivateKey)
testkey.Curve = sm2.P256()
testkey.D = d
testkey.PublicKey.X, testkey.PublicKey.Y = testkey.ScalarBaseMult(testkey.D.Bytes())
// force SM2 sign standard and use default UID
sig, err := testkey.Sign(rand.Reader, toSign, sm2.NewSM2SignerOption(true, nil))
if err != nil {
fmt.Fprintf(os.Stderr, "Error from sign: %s\n", err)
return
}
// Since sign is a randomized function, signature will be
// different each time.
fmt.Printf("%x\n", sig)
}
func ExampleVerifyASN1WithSM2() {
// real public key should be from cert or public key pem file
keypoints, _ := hex.DecodeString("048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1")
testkey := new(ecdsa.PublicKey)
testkey.Curve = sm2.P256()
testkey.X, testkey.Y = elliptic.Unmarshal(testkey.Curve, keypoints)
toSign := []byte("ShangMi SM2 Sign Standard")
signature, _ := hex.DecodeString("304402205b3a799bd94c9063120d7286769220af6b0fa127009af3e873c0e8742edc5f890220097968a4c8b040fd548d1456b33f470cabd8456bfea53e8a828f92f6d4bdcd77")
ok := sm2.VerifyASN1WithSM2(testkey, nil, toSign, signature)
fmt.Printf("%v\n", ok)
// Output: true
}
func ExampleEncryptASN1() {
// real public key should be from cert or public key pem file
keypoints, _ := hex.DecodeString("048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1")
testkey := new(ecdsa.PublicKey)
testkey.Curve = sm2.P256()
testkey.X, testkey.Y = elliptic.Unmarshal(testkey.Curve, keypoints)
secretMessage := []byte("send reinforcements, we're going to advance")
// crypto/rand.Reader is a good source of entropy for randomizing the
// encryption function.
rng := rand.Reader
ciphertext, err := sm2.EncryptASN1(rng, testkey, secretMessage)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from encryption: %s\n", err)
return
}
// Since encryption is a randomized function, ciphertext will be
// different each time.
fmt.Printf("Ciphertext: %x\n", ciphertext)
}
func ExamplePrivateKey_Decrypt() {
ciphertext, _ := hex.DecodeString("308194022100bd31001ce8d39a4a0119ff96d71334cd12d8b75bbc780f5bfc6e1efab535e85a02201839c075ff8bf761dcbe185c9750816410517001d6a130f6ab97fb23337cce150420ea82bd58d6a5394eb468a769ab48b6a26870ca075377eb06663780c920ea5ee0042be22abcf48e56ae9d29ac770d9de0d6b7094a874a2f8d26c26e0b1daaf4ff50a484b88163d04785b04585bb")
// real private key should be from secret storage
privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
d := new(big.Int).SetBytes(privKey)
testkey := new(sm2.PrivateKey)
testkey.Curve = sm2.P256()
testkey.D = d
testkey.PublicKey.X, testkey.PublicKey.Y = testkey.ScalarBaseMult(testkey.D.Bytes())
plaintext, err := testkey.Decrypt(nil, ciphertext, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from decryption: %s\n", err)
return
}
fmt.Printf("Plaintext: %s\n", string(plaintext))
// Output: Plaintext: send reinforcements, we're going to advance
}

View File

@ -8,18 +8,12 @@ import (
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"errors"
"fmt"
"io" "io"
"log"
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
"github.com/emmansun/gmsm/sm2/sm2ec"
"github.com/emmansun/gmsm/sm3" "github.com/emmansun/gmsm/sm3"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
) )
func TestSplicingOrder(t *testing.T) { func TestSplicingOrder(t *testing.T) {
@ -371,21 +365,6 @@ func TestSignVerify(t *testing.T) {
} }
} }
// This is a reference method to force SM2 standard with SDK [crypto.Signer].
func ExamplePrivateKey_Sign_forceSM2() {
toSign := []byte("ShangMi SM2 Sign Standard")
priv, _ := GenerateKey(rand.Reader)
// force SM2 sign standard and use default UID
sig, err := priv.Sign(rand.Reader, toSign, NewSM2SignerOption(true, nil))
if err != nil {
log.Fatalf("%v", err)
}
// use default UID
ok := VerifyASN1WithSM2(&priv.PublicKey, nil, toSign, sig)
fmt.Printf("%v\n", ok)
// Output: true
}
func TestSignVerifyLegacy(t *testing.T) { func TestSignVerifyLegacy(t *testing.T) {
priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
tests := []struct { tests := []struct {
@ -600,51 +579,6 @@ func TestEqual(t *testing.T) {
} }
} }
// This method provide a sample to handle ASN1 ciphertext ends with extra bytes.
func Example_parseCipherASN1EndsWithInvalidBytes() {
// a sample method to get frist ASN1 SEQUENCE data
getFirstASN1Sequence := func(ciphertext []byte) ([]byte, []byte, error) {
input := cryptobyte.String(ciphertext)
var inner cryptobyte.String
if !input.ReadASN1(&inner, asn1.SEQUENCE) {
return nil, nil, errors.New("there are no sequence tag")
}
if len(input) == 0 {
return ciphertext, nil, nil
}
return ciphertext[:len(ciphertext)-len(input)], input, nil
}
ciphertext, _ := hex.DecodeString("3081980220298ED52AE2A0EBA8B7567D54DF41C5F9B310EDFA4A8E15ECCB44EDA94F9F1FC20220116BE33B0833C95D8E5FF9483CD2D7EFF7033C92FE5DEAB6197D809FF1EEE05F042097A90979A6FCEBDE883C2E07E9C286818E694EDE37C3CDAA70E4CD481BE883E00430D62160BB179CB20CE3B5ECA0F5A535BEB6E221566C78FEA92105F71BD37F3F850AD2F86F2D1E35F15E9356557DAC026A")
_, rest, err := getFirstASN1Sequence(ciphertext)
if err != nil || len(rest) != 0 {
log.Fatalf("can't get a complete ASN1 sequence")
}
ciphertext, _ = hex.DecodeString("3081980220298ED52AE2A0EBA8B7567D54DF41C5F9B310EDFA4A8E15ECCB44EDA94F9F1FC20220116BE33B0833C95D8E5FF9483CD2D7EFF7033C92FE5DEAB6197D809FF1EEE05F042097A90979A6FCEBDE883C2E07E9C286818E694EDE37C3CDAA70E4CD481BE883E00430D62160BB179CB20CE3B5ECA0F5A535BEB6E221566C78FEA92105F71BD37F3F850AD2F86F2D1E35F15E9356557DAC026A0000")
seq, rest, err := getFirstASN1Sequence(ciphertext)
if err != nil || len(rest) != 2 {
log.Fatalf("can't get a complete ASN1 sequence")
}
var (
x1, y1 = &big.Int{}, &big.Int{}
c2, c3 []byte
inner cryptobyte.String
)
input := cryptobyte.String(seq)
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
!input.Empty() ||
!inner.ReadASN1Integer(x1) ||
!inner.ReadASN1Integer(y1) ||
!inner.ReadASN1Bytes(&c3, asn1.OCTET_STRING) ||
!inner.ReadASN1Bytes(&c2, asn1.OCTET_STRING) ||
!inner.Empty() {
log.Fatalf("invalid cipher text")
}
}
func TestPublicKeyToECDH(t *testing.T) { func TestPublicKeyToECDH(t *testing.T) {
priv, _ := GenerateKey(rand.Reader) priv, _ := GenerateKey(rand.Reader)
_, err := PublicKeyToECDH(&priv.PublicKey) _, err := PublicKeyToECDH(&priv.PublicKey)
@ -694,32 +628,6 @@ func TestRandomPoint(t *testing.T) {
} }
} }
// This example method is just for reference, it's NOT a standard method for key transmission.
// In general, private key will be encoded/formatted with PKCS8, public key will be encoded/formatted with a SubjectPublicKeyInfo structure
// (see RFC 5280, Section 4.1).
func Example_createKeysFromRawValue() {
key, _ := GenerateKey(rand.Reader)
d := new(big.Int).SetBytes(key.D.Bytes()) // here we do NOT check if the d is in (0, N) or not
// Create private key from *big.Int
keyCopy := new(PrivateKey)
keyCopy.Curve = P256()
keyCopy.D = d
keyCopy.PublicKey.X, keyCopy.PublicKey.Y = keyCopy.ScalarBaseMult(keyCopy.D.Bytes())
if !key.Equal(keyCopy) {
log.Fatalf("private key and copy should be equal")
}
pointBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
// Create public key from point
publicKeyCopy := new(ecdsa.PublicKey)
publicKeyCopy.Curve = P256()
publicKeyCopy.X, publicKeyCopy.Y = elliptic.Unmarshal(publicKeyCopy.Curve, pointBytes)
if !key.PublicKey.Equal(publicKeyCopy) {
log.Fatalf("public key and copy should be equal")
}
}
func BenchmarkGenerateKey_SM2(b *testing.B) { func BenchmarkGenerateKey_SM2(b *testing.B) {
r := bufio.NewReaderSize(rand.Reader, 1<<15) r := bufio.NewReaderSize(rand.Reader, 1<<15)
b.ReportAllocs() b.ReportAllocs()
@ -859,7 +767,7 @@ func BenchmarkLessThan32_P256(b *testing.B) {
} }
func BenchmarkLessThan32_SM2(b *testing.B) { func BenchmarkLessThan32_SM2(b *testing.B) {
benchmarkEncrypt(b, sm2ec.P256(), "encryption standard") benchmarkEncrypt(b, P256(), "encryption standard")
} }
func BenchmarkMoreThan32_P256(b *testing.B) { func BenchmarkMoreThan32_P256(b *testing.B) {
@ -867,5 +775,5 @@ func BenchmarkMoreThan32_P256(b *testing.B) {
} }
func BenchmarkMoreThan32_SM2(b *testing.B) { func BenchmarkMoreThan32_SM2(b *testing.B) {
benchmarkEncrypt(b, sm2ec.P256(), "encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard") benchmarkEncrypt(b, P256(), "encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard")
} }

140
sm4/example_test.go Normal file
View File

@ -0,0 +1,140 @@
package sm4_test
import (
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"github.com/emmansun/gmsm/padding"
"github.com/emmansun/gmsm/sm4"
)
func Example_encryptCBC() {
// 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("sm4 exampleplaintext")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
// CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2.
pkcs7 := padding.NewPKCS7Padding(sm4.BlockSize)
paddedPlainText := pkcs7.Pad(plaintext)
// 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, sm4.BlockSize+len(paddedPlainText))
iv := ciphertext[:sm4.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[sm4.BlockSize:], paddedPlainText)
fmt.Printf("%x\n", ciphertext)
}
func Example_decryptCBC() {
// 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")
ciphertext, _ := hex.DecodeString("4d5a1486bfda1b34447afd5bb852e77a867cc6b726a8a0e0ef9b2c21fffc3a30b42acf504628f65cb3fba339101c98ff")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < sm4.BlockSize {
panic("ciphertext too short")
}
iv := ciphertext[:sm4.BlockSize]
ciphertext = ciphertext[sm4.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(ciphertext, ciphertext)
// Unpad plaintext
pkcs7 := padding.NewPKCS7Padding(sm4.BlockSize)
ciphertext, err = pkcs7.Unpad(ciphertext)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", ciphertext)
// Output: sm4 exampleplaintext
}
func Example_encryptGCM() {
// Load your secret key from a safe place and reuse it across multiple
// Seal/Open 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("exampleplaintext")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err.Error())
}
// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
nonce := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
sm4gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
// You can encode the nonce and ciphertext with your own scheme
ciphertext := sm4gcm.Seal(nil, nonce, plaintext, nil)
fmt.Printf("%x %x\n", nonce, ciphertext)
}
func Example_decryptGCM() {
// Load your secret key from a safe place and reuse it across multiple
// Seal/Open 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")
// You can decode the nonce and ciphertext with your encoding scheme
ciphertext, _ := hex.DecodeString("b7fdece1c6b3dce9cc386e8bc93df0ce496df789166229f14b973b694a4a23c3")
nonce, _ := hex.DecodeString("07d168e0517656ab7131f495")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err.Error())
}
sm4gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
plaintext, err := sm4gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
panic(err.Error())
}
fmt.Printf("%s\n", plaintext)
// Output: exampleplaintext
}