From bf572aef88963732849b8e5ae679e63c6be4aa46 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Fri, 3 Sep 2021 18:44:41 +0200 Subject: [PATCH] stupidgcm: stupidChacha20poly1305.Open: batch C calls in aead_open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gets the decryption speed to the same level as the encryption speed. internal/speed$ benchstat old.txt new.txt name old time/op new time/op delta StupidXchacha-4 732MB/s ± 0% 740MB/s ± 0% ~ (p=1.000 n=1+1) StupidXchachaDecrypt-4 602MB/s ± 0% 741MB/s ± 0% ~ (p=1.000 n=1+1) --- internal/stupidgcm/chacha.c | 120 ++++++++++++++++++++++++----- internal/stupidgcm/chacha.h | 15 ++++ internal/stupidgcm/stupidchacha.go | 68 ++++------------ 3 files changed, 134 insertions(+), 69 deletions(-) diff --git a/internal/stupidgcm/chacha.c b/internal/stupidgcm/chacha.c index e188bfc..05d68af 100644 --- a/internal/stupidgcm/chacha.c +++ b/internal/stupidgcm/chacha.c @@ -9,6 +9,21 @@ static void panic(const char* const msg) __builtin_trap(); } +static const EVP_CIPHER* getEvpCipher(enum aeadType cipherId) +{ + switch (cipherId) { + case aeadTypeChacha: + return EVP_chacha20_poly1305(); + case aeadTypeGcm: + return EVP_aes_256_gcm(); + } + panic("unknown cipherId"); + return NULL; +} + +// We only support 16-byte tags +static const int supportedTagLen = 16; + // https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode int aead_seal( const enum aeadType cipherId, @@ -23,19 +38,9 @@ int aead_seal( unsigned char* const ciphertext, const int ciphertextBufLen) { - const EVP_CIPHER* evpCipher = NULL; - switch (cipherId) { - case aeadTypeChacha: - evpCipher = EVP_chacha20_poly1305(); - break; - case aeadTypeGcm: - evpCipher = EVP_aes_256_gcm(); - break; - default: - panic("unknown cipherId"); - } + const EVP_CIPHER* evpCipher = getEvpCipher(cipherId); - // Create scratch space "context" + // Create scratch space "ctx" EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); if (!ctx) { panic("EVP_CIPHER_CTX_new failed"); @@ -91,20 +96,99 @@ int aead_seal( panic("EVP_EncryptFinal_ex: unexpected length"); } - // We only support 16-byte tags - const int tagLen = 16; - // Get MAC tag and append it to the ciphertext - if (ciphertextLen + tagLen > ciphertextBufLen) { + if (ciphertextLen + supportedTagLen > ciphertextBufLen) { panic("tag overflows output buffer"); } - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagLen, ciphertext + plaintextLen) != 1) { + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, supportedTagLen, ciphertext + plaintextLen) != 1) { panic("EVP_CTRL_AEAD_GET_TAG failed"); } - ciphertextLen += tagLen; + ciphertextLen += supportedTagLen; // Free scratch space EVP_CIPHER_CTX_free(ctx); return ciphertextLen; } + +int aead_open( + const enum aeadType cipherId, + const unsigned char* const ciphertext, + const int ciphertextLen, + const unsigned char* const authData, + const int authDataLen, + unsigned char* const tag, + const int tagLen, + const unsigned char* const key, + const int keyLen, + const unsigned char* const iv, + const int ivLen, + unsigned char* const plaintext, + const int plaintextBufLen) +{ + const EVP_CIPHER* evpCipher = getEvpCipher(cipherId); + + // Create scratch space "ctx" + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + panic("EVP_CIPHER_CTX_new failed"); + } + + // Set cipher + if (EVP_DecryptInit_ex(ctx, evpCipher, NULL, NULL, NULL) != 1) { + panic("EVP_DecryptInit_ex set cipher failed"); + } + + // Check keyLen by trying to set it (fails if keyLen != 32) + if (EVP_CIPHER_CTX_set_key_length(ctx, keyLen) != 1) { + panic("keyLen mismatch"); + } + + // Set IV length so we do not depend on the default + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, ivLen, NULL) != 1) { + panic("EVP_CTRL_AEAD_SET_IVLEN failed"); + } + + // Set key and IV + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1) { + panic("EVP_DecryptInit_ex set key & iv failed"); + } + + // Provide authentication data + int outLen = 0; + if (EVP_DecryptUpdate(ctx, NULL, &outLen, authData, authDataLen) != 1) { + panic("EVP_DecryptUpdate authData failed"); + } + if (outLen != authDataLen) { + panic("EVP_DecryptUpdate authData: unexpected length"); + } + + // Decrypt "ciphertext" into "plaintext" + if (ciphertextLen > plaintextBufLen) { + panic("ciphertextLen overflows output buffer"); + } + if (EVP_DecryptUpdate(ctx, plaintext, &outLen, ciphertext, ciphertextLen) != 1) { + panic("EVP_DecryptUpdate failed"); + } + int plaintextLen = outLen; + + // Check tag + if (tagLen != supportedTagLen) { + panic("unsupported tag length"); + } + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tagLen, tag) != 1) { + panic("EVP_CTRL_AEAD_SET_TAG failed"); + } + if (EVP_DecryptFinal_ex(ctx, plaintext + plaintextLen, &outLen) != 1) { + // authentication failed + return -1; + } + if (outLen != 0) { + panic("EVP_EncryptFinal_ex: unexpected length"); + } + + /* Clean up */ + EVP_CIPHER_CTX_free(ctx); + + return plaintextLen; +} diff --git a/internal/stupidgcm/chacha.h b/internal/stupidgcm/chacha.h index 780350a..a5eac04 100644 --- a/internal/stupidgcm/chacha.h +++ b/internal/stupidgcm/chacha.h @@ -15,3 +15,18 @@ int aead_seal( const int ivLen, unsigned char* const ciphertext, const int ciphertextBufLen); + +int aead_open( + const enum aeadType cipherId, + const unsigned char* const ciphertext, + const int ciphertextLen, + const unsigned char* const authData, + const int authDataLen, + unsigned char* const tag, + const int tagLen, + const unsigned char* const key, + const int keyLen, + const unsigned char* const iv, + const int ivLen, + unsigned char* const plaintext, + const int plaintextBufLen); diff --git a/internal/stupidgcm/stupidchacha.go b/internal/stupidgcm/stupidchacha.go index 18037c6..5073aa3 100644 --- a/internal/stupidgcm/stupidchacha.go +++ b/internal/stupidgcm/stupidchacha.go @@ -6,7 +6,6 @@ import ( "crypto/cipher" "fmt" "log" - "unsafe" "golang.org/x/crypto/chacha20poly1305" ) @@ -118,59 +117,26 @@ func (g *stupidChacha20poly1305) Open(dst, iv, in, authData []byte) ([]byte, err ciphertext := in[:len(in)-tagLen] tag := in[len(in)-tagLen:] - // https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode + res := int(C.aead_open(C.aeadTypeChacha, + (*C.uchar)(&ciphertext[0]), + C.int(len(ciphertext)), + (*C.uchar)(&authData[0]), + C.int(len(authData)), + (*C.uchar)(&tag[0]), + C.int(len(tag)), + (*C.uchar)(&g.key[0]), + C.int(len(g.key)), + (*C.uchar)(&iv[0]), + C.int(len(iv)), + (*C.uchar)(&buf[0]), + C.int(len(buf)))) - // Create scratch space "context" - ctx := C.EVP_CIPHER_CTX_new() - if ctx == nil { - log.Panic("EVP_CIPHER_CTX_new failed") - } - - // Set cipher to AES-256 - if C.EVP_DecryptInit_ex(ctx, C.EVP_chacha20_poly1305(), nil, nil, nil) != 1 { - log.Panic("EVP_DecryptInit_ex I failed") - } - - // Set key and IV - if C.EVP_DecryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 { - log.Panic("EVP_DecryptInit_ex II failed") - } - - // Set expected MAC tag - if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_AEAD_SET_TAG, tagLen, (unsafe.Pointer)(&tag[0])) != 1 { - log.Panic("EVP_CIPHER_CTX_ctrl EVP_CTRL_AEAD_SET_TAG failed") - } - - // Provide authentication data - var resultLen C.int - if C.EVP_DecryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 { - log.Panic("EVP_DecryptUpdate authData failed") - } - if int(resultLen) != len(authData) { - log.Panicf("Unexpected length %d", resultLen) - } - - // Decrypt "ciphertext" into "buf" - if C.EVP_DecryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&ciphertext[0]), C.int(len(ciphertext))) != 1 { - log.Panic("EVP_DecryptUpdate failed") - } - if int(resultLen) != len(ciphertext) { - log.Panicf("Unexpected length %d", resultLen) - } - - // Check MAC - dummy := make([]byte, 16) - res := C.EVP_DecryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen) - if resultLen != 0 { - log.Panicf("Unexpected length %d", resultLen) - } - - // Free scratch space - C.EVP_CIPHER_CTX_free(ctx) - - if res != 1 { + if res < 0 { return nil, ErrAuth } + if res != outLen { + log.Panicf("unexpected length %d", res) + } if inplace { return dst[:len(dst)+outLen], nil