diff --git a/crypto/aes.go b/crypto/aes.go new file mode 100644 index 0000000..502faef --- /dev/null +++ b/crypto/aes.go @@ -0,0 +1,87 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" +) + +type aesBlock struct { + blockCipher + key []byte +} + +func Aes(key []byte) *aesBlock { + return &aesBlock{key: key} +} + +type aesEcbBlock struct { + *aesBlock +} + +func (o *aesBlock) newCipher() (cipher.Block, error) { + return aes.NewCipher(o.key) +} + +func (o *aesBlock) ECB() *aesEcbBlock { + return &aesEcbBlock{o} +} + +var ( + defaultAesIV = make([]byte, aes.BlockSize) +) + +type aesCbcBlock struct { + *aesBlock + iv []byte +} + +func (o *aesBlock) CBC() *aesCbcBlock { + return &aesCbcBlock{ + aesBlock: o, + iv: defaultAesIV, + } +} + +func (o *aesCbcBlock) WithIV(iv []byte) *aesCbcBlock { + o.iv = iv + + return o +} + +func (o *aesEcbBlock) Encrypt(msg []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + return o.ecbEncrypt(block, msg) +} + +func (o *aesEcbBlock) Decrypt(cipherText []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + return o.ecbDecrypt(block, cipherText) +} + +func (o *aesCbcBlock) Encrypt(msg []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + c := cipher.NewCBCEncrypter(block, o.iv) + return o.encrypt(c, msg) +} + +func (o *aesCbcBlock) Decrypt(chiperText []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + c := cipher.NewCBCEncrypter(block, o.iv) + return o.decrypt(c, chiperText) +} diff --git a/crypto/block.go b/crypto/block.go new file mode 100644 index 0000000..00f9890 --- /dev/null +++ b/crypto/block.go @@ -0,0 +1,58 @@ +package crypto + +import ( + "crypto/cipher" + "errors" +) + +type blockCipher struct { +} + +func (b *blockCipher) ecbEncrypt(block cipher.Block, data []byte) ([]byte, error) { + bs := block.BlockSize() + data = PKCS7Padding(data, bs) + + out := make([]byte, len(data)) + dst := out + for len(data) > 0 { + block.Encrypt(dst, data[:bs]) + data = data[bs:] + dst = dst[bs:] + } + + return out, nil +} + +func (b *blockCipher) ecbDecrypt(block cipher.Block, cipherText []byte) ([]byte, error) { + bs := block.BlockSize() + if len(cipherText)%bs != 0 { + return nil, errors.New("DecryptDES crypto/cipher: input not full blocks") + } + + out := make([]byte, len(cipherText)) + dst := out + for len(cipherText) > 0 { + block.Decrypt(dst, cipherText[:bs]) + cipherText = cipherText[bs:] + dst = dst[bs:] + } + + return PKCS7UnPadding(out), nil +} + +func (b *blockCipher) encrypt(block cipher.BlockMode, data []byte) ([]byte, error) { + + data = PKCS7Padding(data, block.BlockSize()) + + out := make([]byte, len(data)) + block.CryptBlocks(out, data) + return out, nil +} + +func (b *blockCipher) decrypt(block cipher.BlockMode, cipherText []byte) ([]byte, error) { + + out := make([]byte, len(cipherText)) + block.CryptBlocks(out, cipherText) + + return PKCS7UnPadding(out), nil +} diff --git a/crypto/des.go b/crypto/des.go new file mode 100644 index 0000000..274d0b6 --- /dev/null +++ b/crypto/des.go @@ -0,0 +1,95 @@ +package crypto + +import ( + "crypto/cipher" + "crypto/des" + "errors" + "strconv" +) + +type desInstance struct { + blockCipher + key []byte +} + +func Des(key []byte) *desInstance { + return &desInstance{key: key} +} + +// 包含des和3des,根据不同的密钥长度计算 +func (o *desInstance) newCipher() (cipher.Block, error) { + switch len(o.key) { + case 8: + return des.NewCipher(o.key) + case 24: + return des.NewTripleDESCipher(o.key) + default: + return nil, errors.New("crypto/des: invalid key size " + strconv.Itoa(len(o.key))) + } +} + +type desEcb struct { + *desInstance +} + +func (o *desInstance) ECB() *desEcb { + return &desEcb{desInstance: o} +} + +func (o *desEcb) Encrypt(data []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + return o.ecbEncrypt(block, data) +} + +func (o *desEcb) Decrypt(cipherText []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + return o.ecbDecrypt(block, cipherText) +} + +var ( + defaultIV = make([]byte, des.BlockSize) +) + +type desCbc struct { + *desInstance + iv []byte +} + +func (o *desInstance) Cbc() *desCbc { + return &desCbc{desInstance: o, iv: defaultIV} +} + +func (o *desCbc) WithIV(iv []byte) *desCbc { + o.iv = iv + + return o +} + +func (o *desCbc) Encrypt(data []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + e := cipher.NewCBCEncrypter(block, o.iv) + return o.encrypt(e, data) +} + +func (o *desCbc) Decrypt(cipherText []byte) ([]byte, error) { + block, err := o.newCipher() + if err != nil { + return nil, err + } + + d := cipher.NewCBCDecrypter(block, o.iv) + + return o.decrypt(d, cipherText) +} diff --git a/crypto/des_test.go b/crypto/des_test.go new file mode 100644 index 0000000..a823c66 --- /dev/null +++ b/crypto/des_test.go @@ -0,0 +1,38 @@ +package crypto_test + +import ( + "crypto/aes" + "crypto/des" + "encoding/hex" + "testing" + + "github.com/charlienet/go-mixed/crypto" +) + +func TestDes(t *testing.T) { + key, _ := hex.DecodeString("0123456789ABCDEF") + msg, _ := hex.DecodeString("F0A2B07E64DD2C25") + + c, err := crypto.Des(key).ECB().Encrypt(msg) + t.Log(hex.EncodeToString(c), err) + + c, err = crypto.Des(key).Cbc().Encrypt(msg) + t.Log(hex.EncodeToString(c), err) + + d, err := crypto.Des(key).Cbc().Decrypt(c) + t.Log(hex.EncodeToString(d), err) +} + +func TestTripleDES(t *testing.T) { + key, _ := hex.DecodeString("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF") + block, err := des.NewTripleDESCipher(key) + t.Log(block, err, block.BlockSize()) +} + +func TestAes(t *testing.T) { + t.Log(aes.BlockSize) + key, _ := hex.DecodeString("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF") + msg, _ := hex.DecodeString("F0A2B07E64DD2C25") + c, err := crypto.Aes(key).ECB().Encrypt(msg) + t.Log(hex.EncodeToString(c), err) +} diff --git a/crypto/ecdsa.go b/crypto/ecdsa.go new file mode 100644 index 0000000..45d8d7d --- /dev/null +++ b/crypto/ecdsa.go @@ -0,0 +1,100 @@ +package crypto + +import ( + "crypto" + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" + "math/big" + "strconv" +) + +type ecdsaOptions struct { + hashOptions + prv *ecdsa.PrivateKey + pub *ecdsa.PublicKey +} + +type Option interface { + apply(*ecdsaOptions) error +} + +type hash2Option interface { + apply(*hashOptions) error +} + +type privateKeyOption []byte + +func (p privateKeyOption) apply(opts *ecdsaOptions) error { + block, _ := pem.Decode(p) + prv, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return err + } + + opts.prv = prv + + return nil +} + +type publicKeyOption []byte + +func (p publicKeyOption) apply(opts *ecdsaOptions) error { + block, _ := pem.Decode(p) + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return err + } + + opts.pub = pub.(*ecdsa.PublicKey) + return nil +} + +type hashOption crypto.Hash + +func (p hashOption) apply(opts *hashOptions) error { + opts.h = crypto.Hash(p) + return nil +} + +func NewEcdsa(h Hash, opts ...Option) (*ecdsaOptions, error) { + i := &ecdsaOptions{} + + sh := crypto.Hash(h) + if !sh.Available() { + return nil, errors.New("unknown hash value " + strconv.Itoa(int(h))) + } + + i.h = sh + + for _, v := range opts { + if err := v.apply(i); err != nil { + return nil, err + } + } + + return i, nil +} + +func WithHash2(h Hash) hash2Option { + return hashOption(h) +} + +func ParsePrivateKey(pem []byte) Option { + return privateKeyOption(pem) +} + +func ParsePublicKey(pem []byte) Option { + return publicKeyOption(pem) +} + +func (opt *ecdsaOptions) Verify(msg, rText, sText []byte) bool { + var r, s big.Int + _ = r.UnmarshalText(rText) + _ = s.UnmarshalText(sText) + + sum := opt.getHash(msg) + + return ecdsa.Verify(opt.pub, sum, &r, &s) +} diff --git a/crypto/ecdsa_test.go b/crypto/ecdsa_test.go new file mode 100644 index 0000000..0187864 --- /dev/null +++ b/crypto/ecdsa_test.go @@ -0,0 +1,35 @@ +package crypto_test + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "fmt" + "testing" + + "github.com/charlienet/go-mixed/crypto" +) + +func TestEsda(t *testing.T) { + + prv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + t.Log(err) + + ecd, err := x509.MarshalECPrivateKey(prv) + t.Log(err) + + secp256r1, _ := asn1.Marshal(asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}) + fmt.Println(string(pem.EncodeToMemory(&pem.Block{Type: "EC PARAMETERS", Bytes: secp256r1}))) + b := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: ecd}) + fmt.Println(string(b)) +} + +func TestSign(t *testing.T) { + ecdsa, err := crypto.NewEcdsa(crypto.SHA1) + t.Log(err) + + _ = ecdsa +} diff --git a/crypto/padding.go b/crypto/padding.go new file mode 100644 index 0000000..65410a1 --- /dev/null +++ b/crypto/padding.go @@ -0,0 +1,15 @@ +package crypto + +import "bytes" + +func PKCS7Padding(src []byte, blockSize int) []byte { + padding := blockSize - len(src)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +func PKCS7UnPadding(src []byte) []byte { + length := len(src) + unpadding := int(src[length-1]) + return src[:(length - unpadding)] +} diff --git a/crypto/rsa.go b/crypto/rsa.go new file mode 100644 index 0000000..3ed67e9 --- /dev/null +++ b/crypto/rsa.go @@ -0,0 +1,116 @@ +package crypto + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "strconv" +) + + + +type rsaInstance struct { + hashOptions + prk *rsa.PrivateKey + puk *rsa.PublicKey +} + +type rsaOption func(o *rsaInstance) error + +func NewRsa(h Hash, opts ...rsaOption) (*rsaInstance, error) { + o := &rsaInstance{} + + sh := crypto.Hash(h) + if !sh.Available() { + return nil, errors.New("unknown hash value " + strconv.Itoa(int(h))) + } + + o.h = sh + + for _, f := range opts { + if err := f(o); err != nil { + return nil, err + } + } + + // 未设置私钥时随机生成密钥 + if o.prk == nil { + prk, err := rsa.GenerateKey(rand.Reader, defaultRsaBits) + if err != nil { + return nil, err + } + + o.prk = prk + } + + // 公钥未设置时从私钥导出 + if o.puk == nil { + o.puk = &o.prk.PublicKey + } + + return o, nil +} + +func ParsePKCS1PrivateKey(p []byte) rsaOption { + return func(o *rsaInstance) error { + block, _ := pem.Decode(p) + if block == nil { + return errors.New("failed to decode private key") + } + + prk, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + + o.prk = prk + + return nil + } +} + +func ParsePKIXPublicKey(p []byte) rsaOption { + return func(o *rsaInstance) error { + block, _ := pem.Decode(p) + if block == nil { + return errors.New("failed to decode public key") + } + + k, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return err + } + + puk := k.(*rsa.PublicKey) + + o.puk = puk + + return nil + } +} + +func (o *rsaInstance) Encrypt(msg []byte) ([]byte, error) { + return rsa.EncryptPKCS1v15(rand.Reader, o.puk, msg) +} + +func (o *rsaInstance) Decrypt(ciphertext []byte) ([]byte, error) { + return rsa.DecryptPKCS1v15(rand.Reader, o.prk, ciphertext) +} + +func (o *rsaInstance) Sign(msg []byte) ([]byte, error) { + hashed := o.getHash(msg) + sign, err := rsa.SignPKCS1v15(rand.Reader, o.prk, o.h, hashed) + return sign, err +} + +func (o *rsaInstance) Verify(msg, sign []byte) bool { + hashed := o.getHash(msg) + if err := rsa.VerifyPKCS1v15(o.puk, o.h, hashed, sign); err != nil { + return false + } + + return true +} diff --git a/crypto/rsa_test.go b/crypto/rsa_test.go new file mode 100644 index 0000000..6feeb69 --- /dev/null +++ b/crypto/rsa_test.go @@ -0,0 +1,129 @@ +package crypto_test + +import ( + "context" + "encoding/base64" + "encoding/hex" + + "testing" + + "github.com/charlienet/go-mixed/crypto" +) + +func TestRsaSign(t *testing.T) { + rsa, err := crypto.NewRsa(crypto.SHA256) + t.Log(rsa, err) + + msg := []byte("123456") + sign, err := rsa.Sign(msg) + t.Log(base64.StdEncoding.EncodeToString(sign)) + t.Log(hex.EncodeToString(sign), err) + + t.Log(rsa.Verify(msg, sign)) +} + +func TestEncrypt(t *testing.T) { + rsa, err := crypto.NewRsa(crypto.SHA1) + t.Log(rsa, err) + + msg := []byte("123456") + cipherText, err := rsa.Encrypt(msg) + t.Log(base64.StdEncoding.EncodeToString(cipherText), err) + + decrypted, err := rsa.Decrypt(cipherText) + t.Log(string(decrypted), err) +} + +const ( + pkBytes = `-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----` + + badPubBytes = `-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDWKn2UPL1SmlufOkgMJHDqLhjf +vyT55z2MZRzeLqB3u8YlRLUD8zi3kmQy6loHQFu0FR7en/DI9EWRXARxMKhbH+CM +yzwmdh9QlzpMrQk0p4e5VtM5pXX9d4B4XxKBXBrmp2n/1D3+oovzD6p37dqqsgPH +xOQ3KQNxnTteS00kzQIDAQAB +-----END PUBLIC KEY-----` +) + +func TestParseKey(t *testing.T) { + rsa, err := crypto.NewRsa( + crypto.SHA1, + crypto.ParsePKCS1PrivateKey([]byte(pkBytes))) + + t.Log(rsa, err) + + msg := []byte("123456") + sign, err := rsa.Sign(msg) + t.Log(base64.StdEncoding.EncodeToString(sign)) + t.Log(hex.EncodeToString(sign), err) + + t.Log(rsa.Verify(msg, sign)) +} + +func TestBadPubKey(t *testing.T) { + + context.TODO() + rsa, err := crypto.NewRsa( + crypto.SHA1, + crypto.ParsePKCS1PrivateKey([]byte(pkBytes)), + crypto.ParsePKIXPublicKey([]byte(badPubBytes))) + + t.Log(rsa, err) + + msg := []byte("123456") + sign, err := rsa.Sign(msg) + t.Log(base64.StdEncoding.EncodeToString(sign)) + t.Log(hex.EncodeToString(sign), err) + + t.Log(rsa.Verify(msg, sign)) +} diff --git a/crypto/sm2.go b/crypto/sm2.go new file mode 100644 index 0000000..f08033b --- /dev/null +++ b/crypto/sm2.go @@ -0,0 +1,109 @@ +package crypto + +import ( + "crypto/rand" + "errors" + "fmt" + + s "github.com/tjfoc/gmsm/sm2" + x "github.com/tjfoc/gmsm/x509" +) + +var ( + defaultMode = C1C3C2 + C1C3C2 = 0 + C1C2C3 = 1 +) + +type sm2Instance struct { + mode int + prk *s.PrivateKey + puk *s.PublicKey +} + +type option func(*sm2Instance) error + +func NewSm2(opts ...option) (*sm2Instance, error) { + o := &sm2Instance{ + mode: defaultMode, + } + + for _, f := range opts { + if err := f(o); err != nil { + return o, err + } + } + + if o.prk == nil { + priv, err := s.GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + + o.prk = priv + } + + if o.puk == nil { + o.puk = &o.prk.PublicKey + } + + return o, nil +} + +func ParseSm2PrivateKey(p []byte, pwd []byte) option { + return func(so *sm2Instance) error { + fmt.Println(string(p)) + + priv, err := x.ReadPrivateKeyFromPem(p, pwd) + if err != nil { + return err + } + + so.prk = priv + return nil + } +} + +func ParseSm2PublicKey(p []byte) option { + return func(so *sm2Instance) error { + pub, err := x.ReadPublicKeyFromPem(p) + if err != nil { + return err + } + + so.puk = pub + return nil + } +} + +func WithMode(mode int) option { + return func(so *sm2Instance) error { + so.mode = mode + return nil + } +} + +func (o *sm2Instance) Encrypt(msg []byte) ([]byte, error) { + return s.Encrypt(o.puk, msg, rand.Reader, o.mode) +} + +func (o *sm2Instance) Decrypt(cipherText []byte) ([]byte, error) { + return s.Decrypt(o.prk, cipherText, o.mode) +} + +func (o *sm2Instance) Sign(msg []byte) ([]byte, error) { + if o.prk == nil { + return []byte{}, errors.New("private key is nil") + } + + b, e := o.prk.Sign(rand.Reader, msg, nil) + return b, e +} + +func (o *sm2Instance) Verify(msg []byte, sign []byte) bool { + if o.puk == nil { + return false + } + + return o.puk.Verify(msg, sign) +} diff --git a/crypto/sm2_test.go b/crypto/sm2_test.go new file mode 100644 index 0000000..4dbd0ac --- /dev/null +++ b/crypto/sm2_test.go @@ -0,0 +1,99 @@ +package crypto_test + +import ( + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "testing" + + "github.com/charlienet/go-mixed/crypto" +) + +func TestNewSm2(t *testing.T) { + o, err := crypto.NewSm2() + t.Logf("%+v, %v", o, err) + + t.Log(crypto.NewSm2(crypto.ParseSm2PrivateKey([]byte{}, []byte{}))) + + msg := []byte("123456") + sign, err := o.Sign(msg) + t.Log(hex.EncodeToString(sign), err) + + ok := o.Verify(msg, sign) + if !ok { + t.Fail() + } + t.Log(ok) +} + +const ( + privPem = `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIH8MFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAgXsd3MYu0BwwICCAAw +DAYIKoZIhvcNAgcFADAdBglghkgBZQMEASoEEJzb8/1Aqhbv2cf777VoW0cEgaAz +DbRJgs76YYpya9wiaZeAavSn8Ydi+CYSvvQurqa1q0Hmna/Lgcgt2Z0F3fFN/EYP +wmDCd6SQ5hdPfQLBtkpDQdFylIHAm26O0smciB7NlfWSdgIluFacbMJ++/YHvcDp +yl1qcRpjk+s+1+8YBUp7Mp1CXbDXdQebH9xezOE3OH8+9zO3qi5qeLEVofgRQJIY +k8EBbLsGMy4WlSr0u29A +-----END ENCRYPTED PRIVATE KEY-----` + + pubPem = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEvfHGxZL/wzWLYgPsHEpFxCCwXKSr +XExvTJS6FAem+lQTyHwOGT+qFf67J77d5y/exn6E5br79nsJkoM/7A72nQ== +-----END PUBLIC KEY-----` + + badPubPem = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE3Og1rzeSs2wO9+YFIdgnAES03u1n +hslcifiQY8173nHtaB3R6T0PwRQTwKbpdec0dwVCpvVcdzHtivndlG0mqQ== +-----END PUBLIC KEY-----` +) + +func TestPrivatePem(t *testing.T) { + signer, err := crypto.NewSm2( + crypto.ParseSm2PrivateKey([]byte(privPem), []byte{}), + crypto.ParseSm2PublicKey([]byte(pubPem))) + + t.Log(signer, err) + if err != nil { + t.Fatal(err) + t.Fail() + } + + msg := []byte("123456") + sign, err := signer.Sign(msg) + t.Log(hex.EncodeToString(sign), err) + + t.Log(signer.Verify(msg, sign)) +} + +func TestBadPublicPem(t *testing.T) { + signer, err := crypto.NewSm2( + crypto.ParseSm2PrivateKey([]byte(privPem), []byte{}), + crypto.ParseSm2PublicKey([]byte(badPubPem))) + + t.Log(signer, err) + + msg := []byte("123456") + sign, err := signer.Sign(msg) + t.Log(hex.EncodeToString(sign), err) + + t.Log(signer.Verify(msg, sign)) +} + +const pemString = `-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAU/RPiFOw8sI+4dM/0ZusJ7dWxi72DpnOukgGNZfPP5oAoGCCqBHM9V +AYItoUQDQgAEbl5hPO00SJnkTpNjefes6QjmOrhQTrcocBQ0V9yB3ow/COroyHIp +MV8UROLaT5kNUim8Z6XQjL+TWrfo11JQ2w== +-----END EC PRIVATE KEY-----` + +func TestDecodePem(t *testing.T) { + + block, _ := pem.Decode([]byte(pemString)) + fmt.Println(string(block.Bytes)) + + prv, err := x509.ParseECPrivateKey(block.Bytes) + t.Log(prv, err) +} diff --git a/crypto/sm4.go b/crypto/sm4.go new file mode 100644 index 0000000..48501fd --- /dev/null +++ b/crypto/sm4.go @@ -0,0 +1,81 @@ +package crypto + +import ( + "sync" + + "github.com/tjfoc/gmsm/sm4" +) + +type sm4Instance struct { + key []byte +} + +func Sm4(key []byte) *sm4Instance { + return &sm4Instance{key: key} +} + +type sm4EcbInstance struct { + *sm4Instance +} + +func (o *sm4Instance) ECB() *sm4EcbInstance { + return &sm4EcbInstance{ + sm4Instance: o, + } +} + +func (o *sm4EcbInstance) Encrypt(msg []byte) ([]byte, error) { + return sm4.Sm4Ecb(o.key, msg, true) +} + +func (o *sm4EcbInstance) Decrypt(cipherText []byte) ([]byte, error) { + return sm4.Sm4Ecb(o.key, cipherText, false) +} + +type sm4CbcInstance struct { + *sm4Instance + iv []byte + lock sync.Mutex +} + +func (o *sm4Instance) CBC() *sm4CbcInstance { + return &sm4CbcInstance{ + sm4Instance: o, + } +} + +func (o *sm4CbcInstance) WithIV(iv []byte) *sm4CbcInstance { + o.iv = iv + + return o +} + +func (o *sm4CbcInstance) Encrypt(msg []byte) ([]byte, error) { + o.lock.Lock() + defer o.lock.Unlock() + + if err := sm4.SetIV(o.iv); err != nil { + return nil, err + } + defer resetIV() + + return sm4.Sm4Cbc(o.key, msg, true) +} + +func (o *sm4CbcInstance) Decrypt(cipherText []byte) ([]byte, error) { + o.lock.Lock() + defer o.lock.Unlock() + + if err := sm4.SetIV(o.iv); err != nil { + return nil, err + } + defer resetIV() + + return sm4.Sm4Cbc(o.key, cipherText, false) +} + +var emptyIV = make([]byte, sm4.BlockSize) + +func resetIV() { + _ = sm4.SetIV(emptyIV) +} diff --git a/crypto/sm4_test.go b/crypto/sm4_test.go new file mode 100644 index 0000000..4efb42d --- /dev/null +++ b/crypto/sm4_test.go @@ -0,0 +1,64 @@ +package crypto_test + +import ( + "encoding/hex" + "testing" + + "github.com/charlienet/go-mixed/crypto" + "github.com/tjfoc/gmsm/sm4" +) + +func TestGmsmSm4(t *testing.T) { + key, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") + msg, _ := hex.DecodeString("F0A2B07E64DD2C2590F93E4EDD90FBB4") + + c, err := sm4.Sm4Ecb(key, msg, true) + t.Log(hex.EncodeToString(c), err) + + d, err := sm4.Sm4Ecb(key, c, false) + t.Log(hex.EncodeToString(d), err) +} + +func TestPadding(t *testing.T) { + msg, _ := hex.DecodeString("F0A2B07E64DD2C2590F93E4EDD90FBB4") + + blockSize := sm4.BlockSize + padding := blockSize - len(msg)%blockSize + t.Log(padding) +} + +func TestSm4(t *testing.T) { + key := []byte("1234567890abcdef") + msg := []byte("123321123321123321123321123321") + + cipherText, err := crypto.Sm4(key).ECB().Encrypt(msg) + t.Log("ECB加密:", hex.EncodeToString(cipherText), err) + + de, err := crypto.Sm4(key).ECB().Decrypt(cipherText) + t.Log("ECB解密:", string(de), err) + + cipherText, err = crypto.Sm4(key).CBC().WithIV(key).Encrypt(msg) + t.Log("CBC加密:", hex.EncodeToString(cipherText), err) + + de, err = crypto.Sm4(key).CBC().WithIV(key).Decrypt(cipherText) + t.Log("CBC解密:", string(de), err) +} + +func TestDecrypt(t *testing.T) { + key := []byte("XbBpuLSzaXtlOYFV") + iv := []byte("UISwD9fW6cFh9SNS") + en := "BAD4C05DB0A51895A38D976F97057C2D1743473CE6DABC3456DD4EA751A9794D81096050DBA084F1CB3791C63DFFEDD1D63B046B155FD06386DEE8434A20D8A7465780EF3660ED1073A253DEA4768AB735E2DDEB4602927D3FF85E429C9B7557E6A3A198F4781642CDD30449968FBD2E54E0425E327805DFB0A1DA4FAE33AC68A3377D20042A9459EEF09BEE8CBE483BF61D32B7BB402730AA2276EA3C3A078B895D684A91DD7EEF0F7A25289B1D4905AF524126E8C3DBCB0AB73C92ABC1A83ECA687777B9B609DD8B0F69602EC3E74243E00B33D51EDF930A5316BCB388E4B7B2A6EFDD8B0BE4A19625D297B25D2BD2E5424F2E9B6A4BBF6A70DBE3C6ABB635554AC21CE053D7ECA23D82EF8060C874D507FC27CFCC06EDF41AF98ED0C2C59E39146CC28BA7630D74870BD372863FC4" + + c := crypto.Sm4(key).CBC().WithIV(iv) + + b, _ := hex.DecodeString(en) + decrypted, err := c.Decrypt(b) + t.Log(hex.EncodeToString(sm4.IV)) + t.Log(hex.EncodeToString(decrypted)) + t.Log(string(decrypted), err) + + encrypted, err := c.Encrypt(decrypted) + t.Log(err) + ddd, err := c.Decrypt(encrypted) + t.Log(string(ddd), err) +} diff --git a/crypto/utils.go b/crypto/utils.go new file mode 100644 index 0000000..56b7e18 --- /dev/null +++ b/crypto/utils.go @@ -0,0 +1,42 @@ +package crypto + +import "crypto" + +type Hash uint + +const ( + MD4 Hash = 1 + iota // import golang.org/x/crypto/md4 + MD5 // import crypto/md5 + SHA1 // import crypto/sha1 + SHA224 // import crypto/sha256 + SHA256 // import crypto/sha256 + SHA384 // import crypto/sha512 + SHA512 // import crypto/sha512 + MD5SHA1 // no implementation; MD5+SHA1 used for TLS RSA + RIPEMD160 // import golang.org/x/crypto/ripemd160 + SHA3_224 // import golang.org/x/crypto/sha3 + SHA3_256 // import golang.org/x/crypto/sha3 + SHA3_384 // import golang.org/x/crypto/sha3 + SHA3_512 // import golang.org/x/crypto/sha3 + SHA512_224 // import crypto/sha512 + SHA512_256 // import crypto/sha512 + BLAKE2s_256 // import golang.org/x/crypto/blake2s + BLAKE2b_256 // import golang.org/x/crypto/blake2b + BLAKE2b_384 // import golang.org/x/crypto/blake2b + BLAKE2b_512 // import golang.org/x/crypto/blake2b + SM3 // import +) + +const ( + defaultRsaBits = 1024 +) + +type hashOptions struct { + h crypto.Hash +} + +func (o *hashOptions) getHash(msg []byte) []byte { + h := o.h.New() + h.Write(msg) + return h.Sum(nil) +}