diff --git a/openssl/aes.go b/openssl/aes.go index 50eed1d..be7d0f3 100644 --- a/openssl/aes.go +++ b/openssl/aes.go @@ -28,9 +28,9 @@ const aesBlockSize = 16 type aesCipher struct { key []byte - enc_ctx *C.EVP_CIPHER_CTX - dec_ctx *C.EVP_CIPHER_CTX - cipher *C.EVP_CIPHER + enc_ctx C.GO_EVP_CIPHER_CTX_PTR + dec_ctx C.GO_EVP_CIPHER_CTX_PTR + cipher C.GO_EVP_CIPHER_PTR } type extraModes interface { @@ -96,8 +96,7 @@ func (c *aesCipher) Encrypt(dst, src []byte) { } } - outlen := C.int(0) - C.go_openssl_EVP_CipherUpdate(c.enc_ctx, (*C.uchar)(unsafe.Pointer(&dst[0])), &outlen, (*C.uchar)(unsafe.Pointer(&src[0])), C.int(aesBlockSize)) + C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), aesBlockSize) runtime.KeepAlive(c) } @@ -119,13 +118,12 @@ func (c *aesCipher) Decrypt(dst, src []byte) { } } - outlen := C.int(0) - C.go_openssl_EVP_CipherUpdate(c.dec_ctx, (*C.uchar)(unsafe.Pointer(&dst[0])), &outlen, (*C.uchar)(unsafe.Pointer(&src[0])), C.int(aesBlockSize)) + C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), aesBlockSize) runtime.KeepAlive(c) } type aesCBC struct { - ctx *C.EVP_CIPHER_CTX + ctx C.GO_EVP_CIPHER_CTX_PTR } func (x *aesCBC) BlockSize() int { return aesBlockSize } @@ -141,12 +139,7 @@ func (x *aesCBC) CryptBlocks(dst, src []byte) { panic("crypto/cipher: output smaller than input") } if len(src) > 0 { - outlen := C.int(0) - if C.go_openssl_EVP_CipherUpdate( - x.ctx, - base(dst), &outlen, - base(src), C.int(len(src)), - ) != C.int(1) { + if C.go_openssl_EVP_CipherUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) != 1 { panic("crypto/cipher: CipherUpdate failed") } runtime.KeepAlive(x) @@ -157,7 +150,7 @@ func (x *aesCBC) SetIV(iv []byte) { if len(iv) != aesBlockSize { panic("cipher: incorrect length IV") } - if C.int(1) != C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, (*C.uchar)(unsafe.Pointer(&iv[0])), -1) { + if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), -1) != 1 { panic("cipher: unable to initialize EVP cipher ctx") } } @@ -165,7 +158,7 @@ func (x *aesCBC) SetIV(iv []byte) { func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { x := new(aesCBC) - var cipher *C.EVP_CIPHER + var cipher C.GO_EVP_CIPHER_PTR switch len(c.key) * 8 { case 128: cipher = C.go_openssl_EVP_aes_128_cbc() @@ -194,7 +187,7 @@ func (c *aesCBC) finalize() { func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { x := new(aesCBC) - var cipher *C.EVP_CIPHER + var cipher C.GO_EVP_CIPHER_PTR switch len(c.key) * 8 { case 128: cipher = C.go_openssl_EVP_aes_128_cbc() @@ -211,7 +204,7 @@ func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { if err != nil { panic(err) } - if C.int(1) != C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) { + if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 { panic("cipher: unable to set padding") } @@ -220,7 +213,7 @@ func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { } type aesCTR struct { - ctx *C.EVP_CIPHER_CTX + ctx C.GO_EVP_CIPHER_CTX_PTR } func (x *aesCTR) XORKeyStream(dst, src []byte) { @@ -233,18 +226,14 @@ func (x *aesCTR) XORKeyStream(dst, src []byte) { if len(src) == 0 { return } - C.go_openssl_EVP_EncryptUpdate_wrapper( - x.ctx, - (*C.uint8_t)(unsafe.Pointer(&dst[0])), - (*C.uint8_t)(unsafe.Pointer(&src[0])), - C.size_t(len(src))) + C.go_openssl_EVP_EncryptUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) runtime.KeepAlive(x) } func (c *aesCipher) NewCTR(iv []byte) cipher.Stream { x := new(aesCTR) - var cipher *C.EVP_CIPHER + var cipher C.GO_EVP_CIPHER_PTR switch len(c.key) * 8 { case 128: cipher = C.go_openssl_EVP_aes_128_ctr() @@ -273,7 +262,7 @@ func (c *aesCTR) finalize() { type aesGCM struct { key []byte tls bool - cipher *C.EVP_CIPHER + cipher C.GO_EVP_CIPHER_PTR } const ( @@ -370,23 +359,23 @@ func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { var encLen C.int // Encrypt additional data. - if C.go_openssl_EVP_EncryptUpdate(ctx, nil, &encLen, base(additionalData), C.int(len(additionalData))) != C.int(1) { + if C.go_openssl_EVP_EncryptUpdate(ctx, nil, &encLen, base(additionalData), C.int(len(additionalData))) != 1 { panic(fail("EVP_CIPHER_CTX_seal")) } // Encrypt plain text. - if C.go_openssl_EVP_EncryptUpdate(ctx, base(out), &encLen, base(plaintext), C.int(len(plaintext))) != C.int(1) { + if C.go_openssl_EVP_EncryptUpdate(ctx, base(out), &encLen, base(plaintext), C.int(len(plaintext))) != 1 { panic(fail("EVP_CIPHER_CTX_seal")) } // Finalise encryption. var encFinalLen C.int - if C.go_openssl_EVP_EncryptFinal_ex(ctx, base(out[encLen:]), &encFinalLen) != C.int(1) { + if C.go_openssl_EVP_EncryptFinal_ex(ctx, base(out[encLen:]), &encFinalLen) != 1 { panic(fail("EVP_CIPHER_CTX_seal")) } encLen += encFinalLen - if C.go_openssl_EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_GET_TAG, C.int(16), unsafe.Pointer(&out[encLen])) != C.int(1) { + if C.go_openssl_EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_GET_TAG, 16, unsafe.Pointer(&out[encLen])) != 1 { panic(fail("EVP_CIPHER_CTX_seal")) } encLen += 16 @@ -435,25 +424,24 @@ func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, er } // Provide any AAD data. - var tmplen C.int - if C.go_openssl_EVP_DecryptUpdate(ctx, nil, &tmplen, base(additionalData), C.int(len(additionalData))) != C.int(1) { + var decLen C.int + if C.go_openssl_EVP_DecryptUpdate(ctx, nil, &decLen, base(additionalData), C.int(len(additionalData))) != 1 { return clearAndFail(errOpen) } // Provide the message to be decrypted, and obtain the plaintext output. - var decLen C.int - if C.go_openssl_EVP_DecryptUpdate(ctx, base(out), &decLen, base(ciphertext), C.int(len(ciphertext))) != C.int(1) { + if C.go_openssl_EVP_DecryptUpdate(ctx, base(out), &decLen, base(ciphertext), C.int(len(ciphertext))) != 1 { return clearAndFail(errOpen) } // Set expected tag value. Works in OpenSSL 1.0.1d and later. - if C.go_openssl_EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_TAG, 16, unsafe.Pointer(&tag[0])) != C.int(1) { + if C.go_openssl_EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_TAG, 16, unsafe.Pointer(&tag[0])) != 1 { return clearAndFail(errOpen) } // Finalise the decryption. var tagLen C.int - if C.go_openssl_EVP_DecryptFinal_ex(ctx, base(out[int(decLen):]), &tagLen) != C.int(1) { + if C.go_openssl_EVP_DecryptFinal_ex(ctx, base(out[int(decLen):]), &tagLen) != 1 { return clearAndFail(errOpen) } @@ -475,12 +463,12 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) { return } -func newCipherCtx(cipher *C.EVP_CIPHER, mode C.int, key, iv []byte) (*C.EVP_CIPHER_CTX, error) { +func newCipherCtx(cipher C.GO_EVP_CIPHER_PTR, mode C.int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { ctx := C.go_openssl_EVP_CIPHER_CTX_new() if ctx == nil { return nil, fail("unable to create EVP cipher ctx") } - if C.int(1) != C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), mode) { + if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), mode) != 1 { return nil, fail("unable to initialize EVP cipher ctx") } return ctx, nil diff --git a/openssl/aes_test.go b/openssl/aes_test.go index 6e06e4f..256bf27 100644 --- a/openssl/aes_test.go +++ b/openssl/aes_test.go @@ -244,7 +244,7 @@ func testDecrypt(t *testing.T, resetNonce bool) { 111, 184, 94, 169, 188, 93, 38, 150, 3, 208, 185, 201, 212, 246, 238, 181, } - if bytes.Compare(expectedCipherText, cipherText) != 0 { + if !bytes.Equal(expectedCipherText, cipherText) { t.Fail() } @@ -257,7 +257,7 @@ func testDecrypt(t *testing.T, resetNonce bool) { t.Fail() } - if bytes.Compare(plainText, decrypted) != 0 { + if !bytes.Equal(plainText, decrypted) { t.Errorf("decryption incorrect\nexp %v, got %v\n", plainText, decrypted) } } @@ -279,3 +279,81 @@ func Test_aesCipher_finalize(t *testing.T) { // in case test execution takes long enough, and it can't be finalized twice. new(aesCipher).finalize() } + +func BenchmarkAES_Encrypt(b *testing.B) { + key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} + in := []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34} + c, err := NewAESCipher(key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(in)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + c.Encrypt(out, in) + } +} + +func BenchmarkAES_Decrypt(b *testing.B) { + key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} + src := []byte{0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32} + c, err := NewAESCipher(key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(src)) + b.SetBytes(int64(len(src))) + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + c.Encrypt(out, src) + } +} + +func BenchmarkAESGCM_Open(b *testing.B) { + const length = 64 + const keySize = 128 / 8 + buf := make([]byte, length) + + b.ReportAllocs() + b.SetBytes(int64(len(buf))) + + var key = make([]byte, keySize) + var nonce [12]byte + var ad [13]byte + c, _ := NewAESCipher(key) + aesgcm, _ := c.(extraModes).NewGCM(gcmStandardNonceSize, gcmTagSize) + var out []byte + + ct := aesgcm.Seal(nil, nonce[:], buf[:], ad[:]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + out, _ = aesgcm.Open(out[:0], nonce[:], ct, ad[:]) + } +} + +func BenchmarkAESGCM_Seal(b *testing.B) { + const length = 64 + const keySize = 128 / 8 + buf := make([]byte, length) + + b.ReportAllocs() + b.SetBytes(int64(len(buf))) + + var key = make([]byte, keySize) + var nonce [12]byte + var ad [13]byte + c, _ := NewAESCipher(key) + aesgcm, _ := c.(extraModes).NewGCM(gcmStandardNonceSize, gcmTagSize) + var out []byte + + ct := aesgcm.Seal(nil, nonce[:], buf[:], ad[:]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + out, _ = aesgcm.Open(out[:0], nonce[:], ct, ad[:]) + } +} diff --git a/openssl/goopenssl.h b/openssl/goopenssl.h index 9483c4f..0f21794 100644 --- a/openssl/goopenssl.h +++ b/openssl/goopenssl.h @@ -35,6 +35,9 @@ void go_openssl_load_functions(void* handle, const void* v1_0_sentinel, const vo #define GO_AES_ENCRYPT 1 #define GO_AES_DECRYPT 0 +typedef void* GO_EVP_CIPHER_PTR; +typedef void* GO_EVP_CIPHER_CTX_PTR; + // Define pointers to all the used OpenSSL functions. // Calling C function pointers from Go is currently not supported. // It is possible to circumvent this by using a C function wrapper. @@ -65,11 +68,25 @@ FOR_ALL_OPENSSL_FUNCTIONS #undef DEFINEFUNC_3_0 #undef DEFINEFUNC_RENAMED -// This wrapper allocate out_len on the C stack, and check that it matches the expected -// value, to avoid having to pass a pointer from Go, which would escape to the heap. -static inline void -go_openssl_EVP_EncryptUpdate_wrapper(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, size_t in_len) +// These wrappers allocate out_len on the C stack to avoid having to pass a pointer from Go, which would escape to the heap. +// Use them only in situations where the output length can be safely discarded. +static inline int +go_openssl_EVP_EncryptUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, uint8_t *out, const uint8_t *in, int in_len) +{ + int len; + return go_openssl_EVP_EncryptUpdate(ctx, out, &len, in, in_len); +} + +static inline int +go_openssl_EVP_DecryptUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, uint8_t *out, const uint8_t *in, int in_len) +{ + int len; + return go_openssl_EVP_DecryptUpdate(ctx, out, &len, in, in_len); +} + +static inline int +go_openssl_EVP_CipherUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, uint8_t *out, const uint8_t *in, int in_len) { int len; - go_openssl_EVP_EncryptUpdate(ctx, out, &len, in, in_len); + return go_openssl_EVP_CipherUpdate(ctx, out, &len, in, in_len); } diff --git a/openssl/openssl_funcs.h b/openssl/openssl_funcs.h index 94652e7..bd2bd4f 100644 --- a/openssl/openssl_funcs.h +++ b/openssl/openssl_funcs.h @@ -87,13 +87,13 @@ DEFINEFUNC(size_t, HMAC_CTX_copy, (HMAC_CTX *dest, HMAC_CTX *src), (dest, src)) DEFINEFUNC_1_1(void, HMAC_CTX_free, (HMAC_CTX * arg0), (arg0)) \ DEFINEFUNC_1_1(HMAC_CTX*, HMAC_CTX_new, (void), ()) \ DEFINEFUNC_1_1(void, HMAC_CTX_reset, (HMAC_CTX * arg0), (arg0)) \ -DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, (void), ()) \ -DEFINEFUNC(int, EVP_CIPHER_CTX_set_padding, (EVP_CIPHER_CTX *x, int padding), (x, padding)) \ +DEFINEFUNC(GO_EVP_CIPHER_CTX_PTR, EVP_CIPHER_CTX_new, (void), ()) \ +DEFINEFUNC(int, EVP_CIPHER_CTX_set_padding, (GO_EVP_CIPHER_CTX_PTR x, int padding), (x, padding)) \ DEFINEFUNC(int, EVP_CipherInit_ex, \ - (EVP_CIPHER_CTX * ctx, const EVP_CIPHER *type, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc), \ + (GO_EVP_CIPHER_CTX_PTR ctx, const GO_EVP_CIPHER_PTR type, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc), \ (ctx, type, impl, key, iv, enc)) \ DEFINEFUNC(int, EVP_CipherUpdate, \ - (EVP_CIPHER_CTX * ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), \ + (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), \ (ctx, out, outl, in, inl)) \ DEFINEFUNC(BIGNUM *, BN_new, (void), ()) \ DEFINEFUNC(void, BN_free, (BIGNUM * arg0), (arg0)) \ @@ -129,31 +129,31 @@ DEFINEFUNC_1_1(void, RSA_get0_key, \ (const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d), \ (rsa, n, e, d)) \ DEFINEFUNC(int, EVP_EncryptInit_ex, \ - (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl, const unsigned char *key, const unsigned char *iv), \ + (GO_EVP_CIPHER_CTX_PTR ctx, const GO_EVP_CIPHER_PTR type, ENGINE *impl, const unsigned char *key, const unsigned char *iv), \ (ctx, type, impl, key, iv)) \ DEFINEFUNC(int, EVP_EncryptUpdate, \ - (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), \ + (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), \ (ctx, out, outl, in, inl)) \ DEFINEFUNC(int, EVP_EncryptFinal_ex, \ - (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl), \ + (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl), \ (ctx, out, outl)) \ DEFINEFUNC(int, EVP_DecryptUpdate, \ - (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \ -DEFINEFUNC(int, EVP_DecryptFinal_ex, (EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl), (ctx, outm, outl)) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_gcm, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_cbc, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_ctr, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_ecb, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_gcm, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_cbc, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_ctr, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_ecb, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_cbc, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_ctr, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_ecb, (void), ()) \ -DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_gcm, (void), ()) \ -DEFINEFUNC(void, EVP_CIPHER_CTX_free, (EVP_CIPHER_CTX* arg0), (arg0)) \ -DEFINEFUNC(int, EVP_CIPHER_CTX_ctrl, (EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr), (ctx, type, arg, ptr)) \ + (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \ +DEFINEFUNC(int, EVP_DecryptFinal_ex, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *outm, int *outl), (ctx, outm, outl)) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_gcm, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_cbc, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_ctr, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_ecb, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_gcm, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_cbc, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_ctr, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_ecb, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_cbc, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_ctr, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_ecb, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_gcm, (void), ()) \ +DEFINEFUNC(void, EVP_CIPHER_CTX_free, (GO_EVP_CIPHER_CTX_PTR arg0), (arg0)) \ +DEFINEFUNC(int, EVP_CIPHER_CTX_ctrl, (GO_EVP_CIPHER_CTX_PTR ctx, int type, int arg, void *ptr), (ctx, type, arg, ptr)) \ DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, (void), ()) \ DEFINEFUNC_RENAMED(int, EVP_PKEY_get_size, EVP_PKEY_size, (const EVP_PKEY *pkey), (pkey)) \ DEFINEFUNC(void, EVP_PKEY_free, (EVP_PKEY * arg0), (arg0)) \