From b3ef950ce2540686daa833d40064a508fd183f0a Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 4 May 2017 20:51:53 +0800 Subject: [PATCH] Go API for GmSSL --- go/build.go | 9 ++++ go/certificate.go | 2 + go/cipher.go | 121 +++++++++++++++++++++++++++++++++++++++++ go/cms.go | 2 + go/digest.go | 77 ++++++++++++++++++++++++++ go/engine.go | 2 + go/ibc.go | 2 + go/kdf.go | 2 + go/mac.go | 76 ++++++++++++++++++++++++++ go/ocsp.go | 2 + go/otp.go | 2 + go/pkey.go | 134 ++++++++++++++++++++++++++++++++++++++++++++++ go/rand.go | 26 +++++++++ go/ssl.go | 2 + go/version.go | 19 +++++++ 15 files changed, 478 insertions(+) create mode 100755 go/build.go create mode 100644 go/certificate.go create mode 100644 go/cipher.go create mode 100644 go/cms.go create mode 100644 go/digest.go create mode 100644 go/engine.go create mode 100644 go/ibc.go create mode 100644 go/kdf.go create mode 100644 go/mac.go create mode 100644 go/ocsp.go create mode 100644 go/otp.go create mode 100644 go/pkey.go create mode 100644 go/rand.go create mode 100644 go/ssl.go create mode 100644 go/version.go diff --git a/go/build.go b/go/build.go new file mode 100755 index 00000000..beb35f56 --- /dev/null +++ b/go/build.go @@ -0,0 +1,9 @@ +/* +build cgo */ + +package gmssl + +/* +#cgo darwin CFLAGS: -I/usr/local/include +#cgo darwin LDFLAGS: -L/usr/local/lib -lcrypto +*/ +import "C" diff --git a/go/certificate.go b/go/certificate.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/certificate.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/cipher.go b/go/cipher.go new file mode 100644 index 00000000..c137b2b1 --- /dev/null +++ b/go/cipher.go @@ -0,0 +1,121 @@ +/* +build cgo */ +package gmssl + +/* +#include +*/ +import "C" + +import ( + "errors" + "fmt" + "runtime" + "unsafe" +) + +func GetCiphers(aliases bool) []string { + return []string{"sms4-cbc", "aes-128-cbc", "aes-256-cbc"} +} + +func GetCipherKeyLength(name string) (int, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + cipher := C.EVP_get_cipherbyname(cname) + if cipher == nil { + return 0, errors.New("Invalid cipher name") + } + return int(C.EVP_CIPHER_key_length(cipher)), nil +} + +func GetCipherBlockSize(name string) (int, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + cipher := C.EVP_get_cipherbyname(cname) + if cipher == nil { + return 0, errors.New("Invalid cipher") + } + return int(C.EVP_CIPHER_block_size(cipher)), nil +} + +func GetCipherIVLength(name string) (int, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + cipher := C.EVP_get_cipherbyname(cname) + if cipher == nil { + return 0, errors.New("Invalid cipher") + } + return int(C.EVP_CIPHER_iv_length(cipher)), nil +} + +type CipherContext struct { + ctx *C.EVP_CIPHER_CTX +} + +func NewCipherContext(name string, args map[string]string, key, iv []byte, encrypt bool) ( + *CipherContext, error) { + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + cipher := C.EVP_get_cipherbyname(cname) + if cipher == nil { + return nil, fmt.Errorf("shit") + } + + if key == nil { + return nil, errors.New("No key") + } + if len(key) != int(C.EVP_CIPHER_key_length(cipher)) { + return nil, errors.New("Invalid key length") + } + + if 0 != int(C.EVP_CIPHER_iv_length(cipher)) { + if iv == nil { + return nil, errors.New("No IV") + } + if len(iv) != int(C.EVP_CIPHER_iv_length(cipher)) { + return nil, errors.New("Invalid IV") + } + } + + ctx := C.EVP_CIPHER_CTX_new() + if ctx == nil { + return nil, fmt.Errorf("shit") + } + + ret := &CipherContext{ctx} + runtime.SetFinalizer(ret, func(ret *CipherContext) { + C.EVP_CIPHER_CTX_free(ret.ctx) + }) + + enc := 1 + if encrypt == false { + enc = 0 + } + + if 1 != C.EVP_CipherInit(ctx, cipher, (*C.uchar)(&key[0]), + (*C.uchar)(&iv[0]), C.int(enc)) { + return nil, fmt.Errorf("shit") + } + + return ret, nil +} + +func (ctx *CipherContext) Update(in []byte) ([]byte, error) { + outbuf := make([]byte, len(in)+int(C.EVP_CIPHER_CTX_block_size(ctx.ctx))) + outlen := C.int(len(outbuf)) + if 1 != C.EVP_CipherUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, + (*C.uchar)(&in[0]), C.int(len(in))) { + return nil, fmt.Errorf("failed to decrypt") + } + return outbuf[:outlen], nil +} + +func (ctx *CipherContext) Final() ([]byte, error) { + outbuf := make([]byte, int(C.EVP_CIPHER_CTX_block_size(ctx.ctx))) + outlen := C.int(len(outbuf)) + if 1 != C.EVP_CipherFinal(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) { + return nil, fmt.Errorf("failed to decrypt") + } + return outbuf[:outlen], nil +} diff --git a/go/cms.go b/go/cms.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/cms.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/digest.go b/go/digest.go new file mode 100644 index 00000000..94d3f951 --- /dev/null +++ b/go/digest.go @@ -0,0 +1,77 @@ +/* +build cgo */ + +package gmssl + +/* +#include +*/ +import "C" + +import ( + "errors" + "fmt" + "runtime" + "unsafe" +) + +func GetDigests(aliases bool) []string { + return []string{"sm3", "sha1", "sha256"} +} + +func GetDigestLength(name string) (int, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + md := C.EVP_get_digestbyname(cname) + if md == nil { + return 0, errors.New("Invalid digest name") + } + return int(C.EVP_MD_size(md)), nil +} + +type DigestContext struct { + ctx *C.EVP_MD_CTX +} + +func NewDigestContext(name string, args map[string]string) (*DigestContext, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + md := C.EVP_get_digestbyname(cname) + if md == nil { + return nil, fmt.Errorf("shit") + } + + ctx := C.EVP_MD_CTX_new() + if ctx == nil { + return nil, fmt.Errorf("shit") + } + + ret := &DigestContext{ctx} + runtime.SetFinalizer(ret, func(ret *DigestContext) { + C.EVP_MD_CTX_free(ret.ctx) + }) + + if 1 != C.EVP_DigestInit(ctx, md) { + return nil, fmt.Errorf("shit") + } + + return ret, nil +} + +func (ctx *DigestContext) Update(data []byte) error { + if len(data) == 0 { + return nil + } + if 1 != C.EVP_DigestUpdate(ctx.ctx, unsafe.Pointer(&data[0]), C.size_t(len(data))) { + errors.New("hello") + } + return nil +} + +func (ctx *DigestContext) Final() ([]byte, error) { + outbuf := make([]byte, 64) + outlen := C.uint(len(outbuf)) + if 1 != C.EVP_DigestFinal(ctx.ctx, (*C.uchar)(unsafe.Pointer(&outbuf[0])), &outlen) { + return nil, errors.New("error") + } + return outbuf[:outlen], nil +} diff --git a/go/engine.go b/go/engine.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/engine.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/ibc.go b/go/ibc.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/ibc.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/kdf.go b/go/kdf.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/kdf.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/mac.go b/go/mac.go new file mode 100644 index 00000000..5b173adc --- /dev/null +++ b/go/mac.go @@ -0,0 +1,76 @@ +/* +build cgo */ + +package gmssl + +/* +#include +#include +*/ +import "C" + +import ( + "errors" + "fmt" + "runtime" + "unsafe" +) + +func GetMacs(aliases bool) []string { + return []string{"hello", "world"} +} + +func GetMacKeyLength(name string) (int, error) { + return 0, nil +} + +func GetMacLength(name string) (int, error) { + return 0, nil +} + +type MACContext struct { + hctx *C.HMAC_CTX +} + +func NewMACContext(name string, args map[string]string, key []byte) (*MACContext, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + md := C.EVP_get_digestbyname(cname) + if md == nil { + return nil, fmt.Errorf("shit") + } + + ctx := C.HMAC_CTX_new() + if ctx == nil { + return nil, fmt.Errorf("shit") + } + + ret := &MACContext{ctx} + runtime.SetFinalizer(ret, func(ret *MACContext) { + C.HMAC_CTX_free(ret.hctx) + }) + + if 1 != C.HMAC_Init_ex(ctx, unsafe.Pointer(&key[0]), C.int(len(key)), md, nil) { + return nil, fmt.Errorf("shit") + } + + return ret, nil +} + +func (ctx *MACContext) Update(data []byte) error { + if len(data) == 0 { + return nil + } + if 1 != C.HMAC_Update(ctx.hctx, (*C.uchar)(unsafe.Pointer(&data[0])), C.size_t(len(data))) { + return errors.New("hello") + } + return nil +} + +func (ctx *MACContext) Final() ([]byte, error) { + outbuf := make([]byte, 64) + outlen := C.uint(len(outbuf)) + if 1 != C.HMAC_Final(ctx.hctx, (*C.uchar)(unsafe.Pointer(&outbuf[0])), &outlen) { + return nil, errors.New("error") + } + return outbuf[:outlen], nil +} diff --git a/go/ocsp.go b/go/ocsp.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/ocsp.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/otp.go b/go/otp.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/otp.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/pkey.go b/go/pkey.go new file mode 100644 index 00000000..7b154cb9 --- /dev/null +++ b/go/pkey.go @@ -0,0 +1,134 @@ +/* +build cgo */ +package gmssl + +/* +#include +*/ +import "C" + +import ( + "errors" +) + +func GetPublicKeyTypes(aliases bool) []string { + return []string{"RSA", "DH", "DSA"} +} + +func GetSignatureSchemes(publicKeyType string, aliases bool) []string { + return []string{"RSA", "DSA", "ECDSA", "SM2"} +} + +func GetPublicKeyEncryptions(publicKeyType string, aliases bool) []string { + return []string{"RSA", "ECIES", "SM2"} +} + +func GetKeyExchanges(publicKeyType string, aliases bool) []string { + return []string{"DH", "ECDH", "SM2"} +} + +type PublicKey struct { + pkey *C.EVP_PKEY +} + +type PrivateKey struct { + pkey *C.EVP_PKEY +} + +func GenerateKeyPair(publicKeyType string, args map[string]string, bits int) (*PublicKey, *PrivateKey, error) { + return nil, nil, errors.New("Not implemented") +} + +func LoadPublicKey(publicKeyType string, args map[string]string, data []byte) (*PublicKey, error) { + return nil, errors.New("Not implemented") +} + +func LoadPrivateKey(publicKeyType string, args map[string]string, data []byte) (*PrivateKey, error) { + return nil, errors.New("Not implemented") +} + +func (pkey *PublicKey) Save(args map[string]string) ([]byte, error) { + return nil, errors.New("Not implemented") +} + +func (pkey *PrivateKey) Save(args map[string]string) ([]byte, error) { + return nil, errors.New("Not implemented") +} + +func (pkey *PublicKey) GetAttributes(args map[string]string) (map[string]string, error) { + return nil, errors.New("Not implemented") +} + +func (pkey *PrivateKey) GetAttributes(args map[string]string) (map[string]string, error) { + return nil, errors.New("Not implemented") +} + +func (pkey *PublicKey) Encrypt(scheme string, args map[string]string, in []byte) ([]byte, error) { + ctx := C.EVP_PKEY_CTX_new(pkey.pkey, nil) + if ctx == nil { + return nil, errors.New("Failure") + } + if 1 != C.EVP_PKEY_encrypt_init(ctx) { + return nil, errors.New("Failurew") + } + outbuf := make([]byte, len(in) + 1024) + outlen := C.size_t(len(outbuf)) + if 1 != C.EVP_PKEY_encrypt(ctx, (*C.uchar)(&outbuf[0]), &outlen, + (*C.uchar)(&in[0]), C.size_t(len(in))) { + return nil, errors.New("Failurew") + } + return outbuf[:outlen], nil +} + +func (pkey *PrivateKey) Decrypt(scheme string, args map[string]string, in []byte) ([]byte, error) { + ctx := C.EVP_PKEY_CTX_new(pkey.pkey, nil) + if ctx == nil { + return nil, errors.New("Failure") + } + if 1 != C.EVP_PKEY_decrypt_init(ctx) { + return nil, errors.New("Failure") + } + outbuf := make([]byte, len(in)) + outlen := C.size_t(len(outbuf)) + if 1 != C.EVP_PKEY_decrypt(ctx, (*C.uchar)(&outbuf[0]), &outlen, + (*C.uchar)(&in[0]), C.size_t(len(in))) { + return nil, errors.New("Failure") + } + return outbuf[:outlen], nil +} + +func (pkey *PrivateKey) Sign(scheme string, args map[string]string, data []byte) ([]byte, error) { + ctx := C.EVP_PKEY_CTX_new(pkey.pkey, nil) + if ctx == nil { + return nil, errors.New("Failure") + } + if 1 != C.EVP_PKEY_sign_init(ctx) { + return nil, errors.New("Failure") + } + outbuf := make([]byte, C.EVP_PKEY_size(pkey.pkey)) + outlen := C.size_t(len(outbuf)) + if 1 != C.EVP_PKEY_sign(ctx, (*C.uchar)(&outbuf[0]), &outlen, + (*C.uchar)(&data[0]), C.size_t(len(data))) { + return nil, errors.New("Failure") + } + return outbuf[:outlen], nil +} + +func (pkey *PublicKey) Verify(scheme string, args map[string]string, data, signature []byte) error { + ctx := C.EVP_PKEY_CTX_new(pkey.pkey, nil) + if ctx == nil { + return errors.New("Failure") + } + if 1 != C.EVP_PKEY_sign_init(ctx) { + return errors.New("Failure") + } + ret := C.EVP_PKEY_verify(ctx, (*C.uchar)(&signature[0]), C.size_t(len(signature)), + (*C.uchar)(&data[0]), C.size_t(len(data))) + if ret != 1 { + return errors.New("Failure") + } + return nil +} + +func (pkey *PrivateKey) DeriveKey(scheme string, args map[string]string, publicKey PublicKey) ([]byte, error) { + return nil, errors.New("Not implemented") +} diff --git a/go/rand.go b/go/rand.go new file mode 100644 index 00000000..64e3deb7 --- /dev/null +++ b/go/rand.go @@ -0,0 +1,26 @@ +/* +build cgo */ +package gmssl + +/* +#include +*/ +import "C" + +import ( + "errors" + "unsafe" +) + +func SeedRandom(seed []byte) error { + C.RAND_seed(unsafe.Pointer(&seed[0]), C.int(len(seed))) + return nil +} + +func GenerateRandom(length int) ([]byte, error) { + outbuf := make([]byte, length) + if 1 != C.RAND_bytes((*C.uchar)(&outbuf[0]), C.int(length)) { + return nil, errors.New("GmSSL Failure") + } + + return outbuf[:length], nil +} diff --git a/go/ssl.go b/go/ssl.go new file mode 100644 index 00000000..0c4464da --- /dev/null +++ b/go/ssl.go @@ -0,0 +1,2 @@ +/* +build cgo */ +package gmssl diff --git a/go/version.go b/go/version.go new file mode 100644 index 00000000..09117fe7 --- /dev/null +++ b/go/version.go @@ -0,0 +1,19 @@ +/* +build cgo */ +package gmssl + +/* +#include +*/ +import "C" + +func GetVersion() []string { + version := []string{ + C.GoString(C.OpenSSL_version(C.OPENSSL_VERSION)), + C.GoString(C.OpenSSL_version(C.OPENSSL_BUILT_ON)), + C.GoString(C.OpenSSL_version(C.OPENSSL_CFLAGS)), + C.GoString(C.OpenSSL_version(C.OPENSSL_PLATFORM)), + C.GoString(C.OpenSSL_version(C.OPENSSL_DIR)), + C.GoString(C.OpenSSL_version(C.OPENSSL_ENGINES_DIR)), + } + return version +}