summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/stupidgcm/chacha.c120
-rw-r--r--internal/stupidgcm/chacha.h15
-rw-r--r--internal/stupidgcm/stupidchacha.go68
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