diff options
author | vnugent <public@vaughnnugent.com> | 2024-08-17 12:18:05 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-08-17 12:18:05 -0400 |
commit | 0925f5c786badb715d564e991d2306632c2aecad (patch) | |
tree | bd46009803c2a136eb85ea9d8566f595ba387291 /src/providers/openssl.c | |
parent | 614c02097f5f173299948df279c2d3e2f9f748f9 (diff) | |
parent | 756a184762db4cb0a9945d066b59d0e2f58c18d8 (diff) |
Merge branch 'develop' into c-sharp
Diffstat (limited to 'src/providers/openssl.c')
-rw-r--r-- | src/providers/openssl.c | 281 |
1 files changed, 228 insertions, 53 deletions
diff --git a/src/providers/openssl.c b/src/providers/openssl.c index 1f31796..c2933fb 100644 --- a/src/providers/openssl.c +++ b/src/providers/openssl.c @@ -2,7 +2,7 @@ * Copyright (c) 2024 Vaughn Nugent * * Package: noscrypt -* File: impl/openssl.c +* File: providers/openssl.c * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -26,6 +26,10 @@ #define _OSSL_FAIL(x) if(!(x)) return CSTATUS_FAIL; +#define ossl_md_sha256() EVP_MD_fetch(NULL, "SHA2-256", NULL) +#define ossl_evp_fetch_chacha20() EVP_CIPHER_fetch(NULL, "ChaCha20", NULL) +#define ossl_mac_fetch_hmac() EVP_MAC_fetch(NULL, "hmac", NULL) + #ifndef _IMPL_SECURE_ZERO_MEMSET #define _IMPL_SECURE_ZERO_MEMSET _ossl_secure_zero_memset @@ -63,11 +67,20 @@ #define _IMPL_CRYPTO_SHA256_DIGEST _ossl_sha256_digest - _IMPLSTB cstatus_t _ossl_sha256_digest(const cspan_t* data, sha256_t digestOut32) + _IMPLSTB cstatus_t _ossl_sha256_digest(cspan_t data, sha256_t digestOut32) { - _overflow_check(data->size) + _overflow_check(data.size); + + DEBUG_ASSERT(digestOut32 != NULL); + DEBUG_ASSERT(ncSpanIsValidC(data)); - _OSSL_FAIL(SHA256(data->data, data->size, digestOut32)) + _OSSL_FAIL( + SHA256( + ncSpanGetOffsetC(data, 0), + ncSpanGetSizeC(data), + digestOut32 + ) + ); return CSTATUS_OK; } @@ -81,29 +94,29 @@ /* Export function */ #define _IMPL_CRYPTO_SHA256_HMAC _ossl_hmac_sha256 - _IMPLSTB cstatus_t _ossl_hmac_sha256(const cspan_t* key, const cspan_t* data, sha256_t hmacOut32) + _IMPLSTB cstatus_t _ossl_hmac_sha256(cspan_t key, cspan_t data, sha256_t hmacOut32) { unsigned int hmacLen; - _overflow_check(key->size) - _overflow_check(data->size) + _overflow_check(key.size) + _overflow_check(data.size) hmacLen = sizeof(sha256_t); _OSSL_FAIL( HMAC( - EVP_sha256(), - key->data, - key->size, - data->data, - data->size, + ossl_md_sha256(), + ncSpanGetOffsetC(key, 0), + ncSpanGetSizeC(key), + ncSpanGetOffsetC(data, 0), + ncSpanGetSizeC(data), hmacOut32, &hmacLen ) - ) + ); /* digest length should match the actual digest size */ - DEBUG_ASSERT(hmacLen == sizeof(sha256_t)) + DEBUG_ASSERT(hmacLen == sizeof(sha256_t)); return CSTATUS_OK; } @@ -112,73 +125,144 @@ #ifndef _IMPL_CRYPTO_SHA256_HKDF_EXPAND - #include <openssl/hmac.h> + #include <openssl/evp.h> #define _IMPL_CRYPTO_SHA256_HKDF_EXPAND _ossl_sha256_hkdf_expand - cstatus_t _ossl_hkdf_update(void* ctx, const cspan_t* data) + struct ossl_hmac_state { + EVP_MAC_CTX* libCtx; + OSSL_PARAM params[2]; + cspan_t prk; + }; + + static cstatus_t _ossl_hmac_init(const struct ossl_hmac_state* osslCtx) { - DEBUG_ASSERT(ctx != NULL) + DEBUG_ASSERT(ncSpanIsValidC(osslCtx->prk)); + DEBUG_ASSERT(osslCtx->params != NULL); - _overflow_check(data->size) + _OSSL_FAIL( + EVP_MAC_init( + osslCtx->libCtx, + ncSpanGetOffsetC(osslCtx->prk, 0), + ncSpanGetSizeC(osslCtx->prk), + osslCtx->params + ) + ); - _OSSL_FAIL(EVP_DigestUpdate((EVP_MD_CTX*)ctx, data->data, data->size)) - return CSTATUS_OK; } - cstatus_t _ossl_hkdf_finish(void* ctx, sha256_t hmacOut32) + static cstatus_t _ossl_hkdf_update(void* ctx, cspan_t data) { - unsigned int hmacSize; + const struct ossl_hmac_state* osslCtx; - DEBUG_ASSERT(ctx != NULL) + DEBUG_ASSERT(ctx != NULL); + _overflow_check(data.size); - hmacSize = sizeof(sha256_t); + osslCtx = (const struct ossl_hmac_state*)ctx; - _OSSL_FAIL(EVP_DigestFinal_ex((EVP_MD_CTX*)ctx, hmacOut32, &hmacSize)) + DEBUG_ASSERT(osslCtx->libCtx != NULL); - /* When configured for sha256, should always be the same size in/out */ - DEBUG_ASSERT(hmacSize == sizeof(sha256_t)) + _OSSL_FAIL( + EVP_MAC_update( + osslCtx->libCtx, + ncSpanGetOffsetC(data, 0), + ncSpanGetSizeC(data) + ) + ); return CSTATUS_OK; } - _IMPLSTB cstatus_t _ossl_sha256_hkdf_expand(const cspan_t* prk, const cspan_t* info, span_t* okm) + static cstatus_t _ossl_hkdf_finish(void* ctx, sha256_t hmacOut32) { - EVP_MD_CTX* ctx; + const struct ossl_hmac_state* osslCtx; + size_t hmacSize; + + DEBUG_ASSERT(ctx != NULL); + DEBUG_ASSERT(hmacOut32 != NULL); + + osslCtx = (const struct ossl_hmac_state*)ctx; + hmacSize = 0; + + DEBUG_ASSERT(osslCtx->libCtx != NULL); + + _OSSL_FAIL( + EVP_MAC_final( + osslCtx->libCtx, + hmacOut32, + &hmacSize, + sizeof(sha256_t) + ) + ); + + /* When configured for sha256, should always be the same size in/out */ + DEBUG_ASSERT(hmacSize == sizeof(sha256_t)); + + /* + * Context must be re-initalized after finalize + * See lifecycle https://docs.openssl.org/3.0/man7/life_cycle-mac/#copyright + */ + + return _ossl_hmac_init(osslCtx); + } + + + _IMPLSTB cstatus_t _ossl_sha256_hkdf_expand(cspan_t prk, cspan_t info, span_t okm) + { + EVP_MAC* mac; cstatus_t result; - struct nc_hkdf_fn_cb_struct handler; + struct ossl_hmac_state hkdfState; + struct nc_hkdf_fn_cb_struct handler; result = CSTATUS_FAIL; + + handler.update = _ossl_hkdf_update; + handler.finish = _ossl_hkdf_finish; + _overflow_check(prk.size); + _overflow_check(info.size); + _overflow_check(okm.size); + + hkdfState.params[0] = OSSL_PARAM_construct_utf8_string("digest", "sha256", 0); + hkdfState.params[1] = OSSL_PARAM_construct_end(); + + hkdfState.prk = prk; + /* - * NOTE! Hmac reusable flag must be set to allow for multiple - * calls to the finish function without losing the context. + * Silly openssl stuff. Enable hmac with sha256 using the system default + * security provider. The one-shot flag must also be disabled (0) because + * we need to call update multiple times. */ - if ((ctx = EVP_MD_CTX_create()) == NULL) + mac = ossl_mac_fetch_hmac(); + + if (mac == NULL) { - return CSTATUS_FAIL; + goto Cleanup; } - if (!EVP_DigestInit_ex2(ctx, EVP_sha256(), NULL)) + hkdfState.libCtx = EVP_MAC_CTX_new(mac); + + if (hkdfState.libCtx == NULL) { goto Cleanup; } - if (!EVP_DigestUpdate(ctx, prk->data, prk->size)) + if (_ossl_hmac_init(&hkdfState) != CSTATUS_OK) { goto Cleanup; } - - handler.update = _ossl_hkdf_update; - handler.finish = _ossl_hkdf_finish; - result = hkdfExpandProcess(&handler, ctx, info, okm); + DEBUG_ASSERT(EVP_MAC_CTX_get_mac_size(hkdfState.libCtx) == sizeof(sha256_t)); - Cleanup: + /* Pass the library */ + result = hkdfExpandProcess(&handler, &hkdfState, info, okm); - EVP_MD_CTX_destroy(ctx); + Cleanup: + + if (hkdfState.libCtx) EVP_MAC_CTX_free(hkdfState.libCtx); + if (mac) EVP_MAC_free(mac); return result; } @@ -191,39 +275,130 @@ #define _IMPL_CHACHA20_CRYPT _ossl_chacha20_crypt - _IMPLSTB cstatus_t _ossl_chacha20_crypt( - const uint8_t* key, - const uint8_t* nonce, - const uint8_t* input, - uint8_t* output, - uint32_t dataLen + _IMPLSTB cstatus_t _ossl_cipher_core( + const EVP_CIPHER* cipher, + cspan_t key, + cspan_t iv, + cspan_t input, + span_t output ) { cstatus_t result; EVP_CIPHER_CTX* ctx; + int tempLen, osslResult; + + DEBUG_ASSERT2(ncSpanGetSize(output) <= ncSpanGetSizeC(input), "Output buffer must be equal or larger than the input buffer"); + DEBUG_ASSERT(cipher != NULL); + + DEBUG_ASSERT((uint32_t)EVP_CIPHER_get_key_length(cipher) == ncSpanGetSizeC(key)); + DEBUG_ASSERT((uint32_t)EVP_CIPHER_iv_length(cipher) == ncSpanGetSizeC(iv)); result = CSTATUS_FAIL; - if ((ctx = EVP_CIPHER_CTX_new()) == NULL) + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) { - return CSTATUS_FAIL; + goto Cleanup; } - if (!EVP_EncryptInit_ex(ctx, EVP_chacha20(), NULL, key, nonce)) + osslResult = EVP_EncryptInit_ex2( + ctx, + cipher, + ncSpanGetOffsetC(key, 0), + ncSpanGetOffsetC(iv, 0), + NULL + ); + + if (!osslResult) { goto Cleanup; } - if (!EVP_EncryptUpdate(ctx, output, (int*)&dataLen, input, dataLen)) + osslResult = EVP_EncryptUpdate( + ctx, + ncSpanGetOffset(output, 0), + &tempLen, + ncSpanGetOffsetC(input, 0), + ncSpanGetSizeC(input) + ); + + if (!osslResult) { goto Cleanup; } + /* + * We can't get a pointer outside the range of the + * output buffer + */ + if (((uint32_t)tempLen) < ncSpanGetSize(output)) + { + if (!EVP_EncryptFinal_ex(ctx, ncSpanGetOffset(output, tempLen), &tempLen)) + { + goto Cleanup; + } + } + result = CSTATUS_OK; Cleanup: - EVP_CIPHER_CTX_free(ctx); + if (ctx) EVP_CIPHER_CTX_free(ctx); + + return result; + } + + _IMPLSTB cstatus_t _ossl_chacha20_crypt( + const uint8_t* key, + const uint8_t* nonce, + const uint8_t* input, + uint8_t* output, + uint32_t dataLen + ) + { + cstatus_t result; + EVP_CIPHER* cipher; + uint8_t chaChaIv[CHACHA_NONCE_SIZE + 4]; + cspan_t keySpan, nonceSpan, inputSpan; + span_t outputSpan; + + result = CSTATUS_FAIL; + + /* + * RFC 7539 ChaCha20 requires a 16 byte initialization vector. A + * counter value is preprended to the nonce to make up the 16 byte + * size. + * + * The counter is always set to 0 for the nonce. + */ + + ncCryptoSecureZero(chaChaIv, sizeof(chaChaIv)); + MEMMOV(chaChaIv + 4, nonce, CHACHA_NONCE_SIZE); + + ncSpanInitC(&keySpan, key, CHACHA_KEY_SIZE); + ncSpanInitC(&nonceSpan, chaChaIv, sizeof(chaChaIv)); + ncSpanInitC(&inputSpan, input, dataLen); + ncSpanInit(&outputSpan, output, dataLen); + + cipher = ossl_evp_fetch_chacha20(); + + if (cipher == NULL) + { + goto Cleanup; + } + + result = _ossl_cipher_core( + cipher, + keySpan, + nonceSpan, + inputSpan, + outputSpan + ); + + Cleanup: + + if (cipher) EVP_CIPHER_free(cipher); return result; } |