diff options
Diffstat (limited to 'internal/stupidgcm')
| -rw-r--r-- | internal/stupidgcm/chacha.c | 120 | ||||
| -rw-r--r-- | internal/stupidgcm/chacha.h | 15 | ||||
| -rw-r--r-- | 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 - -	// 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) +	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)))) -	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 | 
