/* * SRT - Secure, Reliable, Transport * Copyright (c) 2019 Haivision Systems Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ /***************************************************************************** written by Haivision Systems Inc. 2022-05-19 (jdube) OpenSSL EVP CRYSPR/4SRT (CRYypto Service PRovider for SRT). *****************************************************************************/ #include "hcrypt.h" #include typedef struct tag_crysprOpenSSL_EVP_cb { CRYSPR_cb ccb; /* Add cryptolib specific data here */ } crysprOpenSSL_EVP_cb; int crysprOpenSSL_EVP_Prng(unsigned char* rn, int len) { return (RAND_bytes(rn, len) <= 0 ? -1 : 0); } const EVP_CIPHER* (*Xcipher_fnptr)(void) = EVP_aes_128_ecb; const EVP_CIPHER* (*_crysprOpenSSL_EVP_cipher_fnptr[][3])(void) = { {NULL, NULL, NULL}, // HCRYPT_CTX_MODE_CLRTXT {EVP_aes_128_ecb, EVP_aes_192_ecb, EVP_aes_256_ecb}, // HCRYPT_CTX_MODE_AESECB {EVP_aes_128_ctr, EVP_aes_192_ctr, EVP_aes_256_ctr}, // HCRYPT_CTX_MODE_AESCTR {NULL, NULL, NULL}, // HCRYPT_CTX_MODE_AESCBC {EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm}, // HCRYPT_CTX_MODE_AESGCM }; int crysprOpenSSL_EVP_AES_SetKey( int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR] */ bool bEncrypt, /* true Enxcrypt key, false: decrypt */ const unsigned char* kstr, /* key sttring*/ size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128, AES192, or AES256) */ CRYSPR_AESCTX* aes_key) /* CRYpto Service PRovider AES Key context */ { const EVP_CIPHER* cipher = NULL; int idxKlen = (int)((kstr_len / 8) - 2); /* key_len index in cipher_fnptr array in [0,1,2] range */ switch (cipher_type) { case HCRYPT_CTX_MODE_CLRTXT: return 0; case HCRYPT_CTX_MODE_AESECB: break; case HCRYPT_CTX_MODE_AESCTR: #if !CRYSPR_HAS_AESCTR /* internal implementation of AES-CTR using crypto lib's AES-ECB */ cipher_type = HCRYPT_CTX_MODE_AESECB; #endif break; case HCRYPT_CTX_MODE_AESGCM: break; default: HCRYPT_LOG(LOG_ERR, "invalid cipher type (%d). Expected: [%d..%d]\n", cipher_type, HCRYPT_CTX_MODE_AESECB, HCRYPT_CTX_MODE_AESCTR); return (-1); } switch (kstr_len) { case 128 / 8: case 192 / 8: case 256 / 8: break; default: HCRYPT_LOG(LOG_ERR, "invalid key length (%d). Expected: 16, 24, 32\n", (int)kstr_len); return -1; } cipher = _crysprOpenSSL_EVP_cipher_fnptr[cipher_type][idxKlen](); if (bEncrypt) { /* Encrypt key */ if (!EVP_EncryptInit_ex(aes_key, cipher, NULL, kstr, NULL)) { HCRYPT_LOG(LOG_ERR, "%s", "EVP_CipherInit_ex(kek) failed\n"); return (-1); } } else { /* Decrypt key */ if (!EVP_DecryptInit_ex(aes_key, cipher, NULL, kstr, NULL)) { HCRYPT_LOG(LOG_ERR, "%s", "EVP_CipherInit_ex(kek) failed\n"); return (-1); } } return (0); } static CRYSPR_cb* crysprOpenSSL_EVP_Open(CRYSPR_methods* cryspr, size_t max_len) { CRYSPR_cb* cryspr_cb = crysprHelper_Open(cryspr, sizeof(*cryspr_cb), max_len); if (NULL == cryspr_cb) { HCRYPT_LOG(LOG_ERR, "crysprFallback_Open(%p, %zd) failed\n", cryspr, max_len); return (NULL); } cryspr_cb->aes_kek = EVP_CIPHER_CTX_new(); cryspr_cb->aes_sek[0] = EVP_CIPHER_CTX_new(); cryspr_cb->aes_sek[1] = EVP_CIPHER_CTX_new(); return (cryspr_cb); } static int crysprOpenSSL_EVP_Close(CRYSPR_cb* cryspr_cb) { if (NULL != cryspr_cb) { EVP_CIPHER_CTX_free(cryspr_cb->aes_sek[0]); EVP_CIPHER_CTX_free(cryspr_cb->aes_sek[1]); EVP_CIPHER_CTX_free(cryspr_cb->aes_kek); } return (crysprHelper_Close(cryspr_cb)); } #if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encrypt, false:decrypt */ CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */ const unsigned char* indata, /* src (clear text if encrypt, cipher text otherwise)*/ size_t inlen, /* indata length */ unsigned char* out_txt, /* dst (cipher text if encrypt, clear text otherwise) */ size_t* outlen_p) /* in/out dst len */ { int nmore = inlen % CRYSPR_AESBLKSZ; /* bytes in last incomplete block */ int nblk = (int)(inlen / CRYSPR_AESBLKSZ + (nmore ? 1 : 0)); /* blocks including incomplete */ size_t outsiz = (outlen_p ? *outlen_p : 0); int c_len = 0, f_len = 0; (void)bEncrypt; // not needed, alreadydefined in context if (outsiz % CRYSPR_AESBLKSZ) { HCRYPT_LOG(LOG_ERR, "%s\n", "EcbCipher() no room for PKCS7 padding"); return (-1); /* output buf size must be a multiple of AES block size (16) */ } if ((outsiz > 16) && ((int)outsiz < (nblk * CRYSPR_AESBLKSZ))) { HCRYPT_LOG(LOG_ERR, "%s\n", "EcbCipher() no room for PKCS7 padding"); return (-1); /* output buf size must have room for PKCS7 padding */ } /* allows reusing of 'e' for multiple encryption cycles */ if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, NULL, bEncrypt)) { HCRYPT_LOG(LOG_ERR, "EVP_CipherInit_ex(%p,NULL,...,-1) failed\n", aes_key); return -1; } if (!EVP_CIPHER_CTX_set_padding(aes_key, 0)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_set_padding(%p) failed", aes_key); return -1; } /* update ciphertext, c_len is filled with the length of ciphertext generated, * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes */ if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int)inlen)) { HCRYPT_LOG(LOG_ERR, "EVP_CipherUpdate(%p, out, %d, in, %d) failed\n", aes_key, c_len, inlen); return -1; } /* update ciphertext with the final remaining bytes */ /* Useless with pre-padding */ f_len = 0; if (0 == EVP_CipherFinal_ex(aes_key, &out_txt[c_len], &f_len)) { #if ENABLE_HAICRYPT_LOGGING char szErrBuf[256]; HCRYPT_LOG(LOG_ERR, "EVP_CipherFinal_ex(ctx,&out[%d],%d)) failed: %s\n", c_len, f_len, ERR_error_string(ERR_get_error(), szErrBuf)); #endif /*ENABLE_HAICRYPT_LOGGING*/ return -1; } if (outlen_p != NULL) *outlen_p = nblk * CRYSPR_AESBLKSZ; return 0; } #endif /* !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) */ int crysprOpenSSL_EVP_AES_CtrCipher(bool bEncrypt, /* true:encrypt, false:decrypt */ CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */ unsigned char* iv, /* iv */ const unsigned char* indata, /* src */ size_t inlen, /* length */ unsigned char* out_txt) /* dest */ { int c_len, f_len; (void)bEncrypt; /* allows reusing of 'e' for multiple encryption cycles */ if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, iv, -1)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherInit_ex() failed"); return -1; } if (!EVP_CIPHER_CTX_set_padding(aes_key, 0)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_set_padding() failed"); return -1; } /* update ciphertext, c_len is filled with the length of ciphertext generated, * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes */ if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int)inlen)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() failed"); return -1; } /* update ciphertext with the final remaining bytes */ /* Useless with pre-padding */ f_len = 0; if (0 == EVP_CipherFinal_ex(aes_key, &out_txt[c_len], &f_len)) { #if ENABLE_HAICRYPT_LOGGING char szErrBuf[256]; HCRYPT_LOG(LOG_ERR, "EVP_CipherFinal_ex(ctx,&out[%d],%d)) failed: %s\n", c_len, f_len, ERR_error_string(ERR_get_error(), szErrBuf)); #endif /*ENABLE_HAICRYPT_LOGGING*/ return -1; } return 0; } int crysprOpenSSL_EVP_AES_GCMCipher(bool bEncrypt, /* true:encrypt, false:decrypt */ CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */ unsigned char* iv, /* iv */ const unsigned char* aad, /* associated data */ size_t aadlen, const unsigned char* indata, /* src */ size_t inlen, /* length */ unsigned char* out_txt, unsigned char* out_tag) /* auth tag */ { int c_len, f_len; /* allows reusing of 'e' for multiple encryption cycles */ if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, iv, -1)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherInit_ex() failed"); return -1; } if (!EVP_CIPHER_CTX_set_padding(aes_key, 0)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_set_padding() failed"); return -1; } /* * Provide any AAD data. This can be called zero or more times as * required */ if (1 != EVP_CipherUpdate(aes_key, NULL, &c_len, aad, (int) aadlen)) { ERR_print_errors_fp(stderr); HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_EncryptUpdate failed"); return -1; } /* update ciphertext, c_len is filled with the length of ciphertext generated, * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes */ if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int) inlen)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() failed"); return -1; } if (!bEncrypt && !EVP_CIPHER_CTX_ctrl(aes_key, EVP_CTRL_GCM_SET_TAG, HAICRYPT_AUTHTAG_MAX, out_tag)) { ERR_print_errors_fp(stderr); HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_EncryptUpdate failed"); return -1; } /* update ciphertext with the final remaining bytes */ /* Useless with pre-padding */ f_len = 0; if (0 == EVP_CipherFinal_ex(aes_key, &out_txt[c_len], &f_len)) { #if ENABLE_HAICRYPT_LOGGING char szErrBuf[256]; HCRYPT_LOG(LOG_ERR, "EVP_CipherFinal_ex(ctx,&out[%d],%d)) failed: %s\n", c_len, f_len, ERR_error_string(ERR_get_error(), szErrBuf)); #endif /*ENABLE_HAICRYPT_LOGGING*/ return -1; } /* Get the tag if we are encrypting */ if (bEncrypt && !EVP_CIPHER_CTX_ctrl(aes_key, EVP_CTRL_GCM_GET_TAG, HAICRYPT_AUTHTAG_MAX, out_tag)) { ERR_print_errors_fp(stderr); HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_GET_TAG) failed"); return -1; } return 0; } /* * Password-based Key Derivation Function */ int crysprOpenSSL_EVP_KmPbkdf2(CRYSPR_cb* cryspr_cb, char* passwd, /* passphrase */ size_t passwd_len, /* passphrase len */ unsigned char* salt, /* salt */ size_t salt_len, /* salt_len */ int itr, /* iterations */ size_t key_len, /* key_len */ unsigned char* out) /* derived key */ { (void)cryspr_cb; int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd, (int)passwd_len, salt, (int)salt_len, itr, (int)key_len, out); return (rc == 1 ? 0 : -1); } #if CRYSPR_HAS_AESKWRAP int crysprOpenSSL_EVP_KmWrap(CRYSPR_cb* cryspr_cb, unsigned char* wrap, const unsigned char* sek, unsigned int seklen) { crysprOpenSSL_EVP_cb* aes_data = (crysprOpenSSL_EVP_cb*)cryspr_cb; EVP_CIPHER_CTX* kek = CRYSPR_GETKEK(cryspr_cb); // key encrypting key return (((seklen + HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_wrap_key(kek, NULL, wrap, sek, seklen)) ? 0 : -1); } int crysprOpenSSL_EVP_KmUnwrap(CRYSPR_cb* cryspr_cb, unsigned char* sek, // Stream encrypting key const unsigned char* wrap, unsigned int wraplen) { crysprOpenSSL_EVP_cb* aes_data = (crysprOpenSSL_EVP_cb*)cryspr_cb; EVP_CIPHER_CTX* kek = CRYSPR_GETKEK(cryspr_cb); // key encrypting key return (((wraplen - HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_unwrap_key(kek, NULL, sek, wrap, wraplen)) ? 0 : -1); } #endif /*CRYSPR_HAS_AESKWRAP*/ static CRYSPR_methods crysprOpenSSL_EVP_methods; CRYSPR_methods* crysprOpenSSL_EVP(void) { if (NULL == crysprOpenSSL_EVP_methods.open) { crysprInit(&crysprOpenSSL_EVP_methods); // Default/fallback methods crysprOpenSSL_EVP_methods.prng = crysprOpenSSL_EVP_Prng; //--CryptoLib Primitive API----------------------------------------------- crysprOpenSSL_EVP_methods.aes_set_key = crysprOpenSSL_EVP_AES_SetKey; #if CRYSPR_HAS_AESCTR crysprOpenSSL_EVP_methods.aes_ctr_cipher = crysprOpenSSL_EVP_AES_CtrCipher; #endif crysprOpenSSL_EVP_methods.aes_gcm_cipher = crysprOpenSSL_EVP_AES_GCMCipher; #if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) /* AES-ECB only required if cryspr has no AES-CTR and no AES KeyWrap */ /* OpenSSL has both AESCTR and AESKWRP and the AESECB wrapper is only used to test the falback methods */ crysprOpenSSL_EVP_methods.aes_ecb_cipher = crysprOpenSSL_EVP_AES_EcbCipher; #endif #if !CRYSPR_HAS_PBKDF2 crysprOpenSSL_EVP_methods.sha1_msg_digest = NULL; // Required to use eventual default/fallback KmPbkdf2 #endif //--Crypto Session API----------------------------------------- crysprOpenSSL_EVP_methods.open = crysprOpenSSL_EVP_Open; crysprOpenSSL_EVP_methods.close = crysprOpenSSL_EVP_Close; //--Keying material (km) encryption #if CRYSPR_HAS_PBKDF2 crysprOpenSSL_EVP_methods.km_pbkdf2 = crysprOpenSSL_EVP_KmPbkdf2; #else #error There is no default/fallback method for PBKDF2 #endif // crysprOpenSSL_EVP_methods.km_setkey = #if CRYSPR_HAS_AESKWRAP crysprOpenSSL_EVP_methods.km_wrap = crysprOpenSSL_EVP_KmWrap; crysprOpenSSL_EVP_methods.km_unwrap = crysprOpenSSL_EVP_KmUnwrap; #endif //--Media stream (ms) encryption // crysprOpenSSL_EVP_methods.ms_setkey = // crysprOpenSSL_EVP_methods.ms_encrypt = // crysprOpenSSL_EVP_methods.ms_decrypt = } return (&crysprOpenSSL_EVP_methods); }